Porting AVR code for MSP430 chips

I’ve just finished my first port of code from AVR over to MSP430. I used the garage door code button source because it’s fresh on my mind and I can reproduce the hardware on the TI Launchpad board. This provided a few sets of challenges, and showed me what I can do from the start to make my code more portable. I’ll get into both of those subjects and share the ported code after the break.

My first step was to convert the function that starts the timer. I had been using a 10ms interrupt to run some code and increment a variable. It wasn’t hard to get the MSP430G2231 to do that. Its TimerA is a 16-bit timer (this is a 16-bit chip after all) and has the option to use a divider. But if the system clock is running at 1MHz, and I want 100 interrupts per second, I just needed to run the timer directly from the system clock and interrupt at 10,000.

Next, I made changes to the I/O setup for the buttons, LED, and the load. This is pedantic so I’m just going to light the green LED to simulate the load, the red LED for the status light, the onboard switch for the user button, and a piece of wire on P1.4 to short to ground as a programming button. The LEDs don’t require too much change. The AVR defines used just a pin number, and the MSP430 defines used a bit shifted value like: (1<<5), so I had to account for that.

There was just a bit of initialization rewriting required for the buttons. That’s because I had been using the AVR internal pull-up resistors. MSP430 chips have internal pull-up OR pull-down resistors, requiring one extra line of code for that selection.

The real problem issue was that I stored the entry code in EEPROM on the AVR chip. There’s no built-in EEPROM with the MSP430 line, so how can we store the code through power cycles? Flash memory.

The MSP430G2231 has 2kB of program memory but it also has 256 bytes of Info Flash. This memory works a bit differently than internal EEPROM, and made me realize that I should have used separate functions for the EEPROM reads and writes. When porting the code I broke out the save and retrieve operations by adding these functions:

  • void get_code_from_flash(void);
  • void store_code(void);
  • void clear_flash(void);

The info flash is a bit peculiar to me. I guess all flash memory works this way but I haven’t dealt with it before. Apparently bits can only be written when you are changing them from 1 to 0. If you need a 0 to become a 1 you must erase the entire 64 kB block first. It’s not a huge deal as long as you know.

After studying the datasheets, looking at the app notes, and testing out the sample code I did manage to get it to work. But I found that I could NEVER get the first byte of a block to write. I have no idea why but I’ve got a forum thread going about it. I worked around this by offsetting my pointer by 1 byte.

That’s about it. The code works quite well on the Launchpad. All-in-all I find porting between these two architectures quite simple. One of the problems I am facing is that the MSP430 chips have a max operating voltage of 3.3V and most of the parts I have laying around are 5V. I’ll have to get working on that issue.

/*--------------------------------------------------------------------------
Garage Entry
Copyright (c) 2010 Mike Szczys

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the &quot;Software&quot;), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------
  This project uses an MSP430G2231 to take entry from a single button and
  display status with a single LED. A proper code entry will trigger a
  relay and open a garage door. There is functionality for changing the
  default code using a programming button. The original project use an
  AVR ATtiny13:

http://jumptuck.wordpress.com/2010/08/01/garage-door-code-button/

  This has been ported for use with the MSP-EXP430G2 Launchpad board using
  the value line G2231 chip. Unlike AVR chips, there is no onboard EEPROM
  with the TI chips. This required one portion of the code to be overhauled.
  The code is now saved to Section B of the Info memory. This memory when
  erased reads back 0xFF. Software can program this Info Flash, changing the
  appropriate bits from 1 to 0 but it's can't change 0 bits to 1. Because
  of this, then entire segment needs to be erased every time data is written
  to that segment.

--------------------------------------------------------------------------*/

#define F_CPU 1000000

#include &lt;io.h&gt;
#include &lt;signal.h&gt;
//#include &lt;avr/eeprom.h&gt;

//Set default security code here
unsigned char code1 = 1;
unsigned char code2 = 2;
unsigned char code3 = 3;
unsigned char code4 = 4;

#define KEY_DDR		P1DIR
#define KEY_PORT	P1OUT
#define KEY_REN		P1REN
#define KEY_PIN		P1IN
#define KEY0		BIT3	//User button
#define KEY1		BIT4	//Programming jumper

#define LED_DDR		P1DIR
#define LED_PORT	P1OUT
#define LED0		BIT0

#define LOAD_DDR	P1DIR
#define LOAD_PORT	P1OUT
#define LOAD0		BIT6

//State aliases
#define STATE_REST		0
#define STATE_ENTRY		1
#define STATE_PROGRAM_WAIT	2
#define STATE_PROGRAM 		3

//System
volatile unsigned char systick = 0;

unsigned char entry_index = 0;
unsigned char entry_code[4];

//Debounce
unsigned char debounce_cnt = 0;
volatile unsigned char key_press;
unsigned char key_state;

/*--------------------------------------------------------------------------
  Prototypes
--------------------------------------------------------------------------*/
unsigned char get_key_press( unsigned char key_mask );
void init_timers(void);
void init_io(void);
void delay_ms(unsigned int n);
void blink_LED(unsigned char times, unsigned int duration_ms);
void initialize_entry_code(void);
unsigned char system_reset(unsigned char state);
void readback(void);
void check_code(unsigned char state);
void get_code_from_flash(void);
void store_code(void);
void clear_flash(void);

/*--------------------------------------------------------------------------
  FUNC: 8/12/10 - Used to read debounced button presses
  PARAMS: A keymask corresponding to the pin for the button you with to poll
  RETURNS: A keymask where any high bits represent a button press
--------------------------------------------------------------------------*/
unsigned char get_key_press( unsigned char key_mask )
{
  dint();			// read and clear atomic !
  key_mask &amp;= key_press;	// read key(s)
  key_press ^= key_mask;	// clear key(s)
  eint();
  return key_mask;
}

/*--------------------------------------------------------------------------
  FUNC: 8/12/10 - Sets and starts a system timer
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_timers(void)
{
  // If calibration constants have not been erased
  if (CALBC1_1MHZ != 0xFF &amp;&amp; CALDCO_1MHZ != 0xFF)
  {
    BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
    DCOCTL = CALDCO_1MHZ;
  }

  dint();
  //TimerA for buttons and systick
  TACTL = TASSEL_2 | MC_1;		//SMCLK in UP mode
  TACCTL0 |= CCIE;			//enable timer interrupt
  TACCR0 = F_CPU / 100;  //set the TimerA match for 10ms interrupts
  eint();				//global interrupt enable
}

/*--------------------------------------------------------------------------
  FUNC: 8/12/10 - Initialize input and output registers
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_io(void)
{
  //Setup Button
  KEY_DDR &amp;= ~(KEY0 | KEY1);	//set pins as inputs
  KEY_REN |= (KEY0 | KEY1);	//enable resistor
  KEY_PORT |= (KEY0 | KEY1);	//set resistor to pull-up

  //Setup LED
  LED_DDR |= LED0;
  LED_PORT &amp;= ~(LED0);

  //Setup Load (relay pin)
  LOAD_DDR |= LOAD0;
  LOAD_PORT &amp;= ~(LOAD0);
}

/*--------------------------------------------------------------------------
  FUNC: 7/23/10 - Wastes approximately n milliseconds
  PARAMS: Duration in milliseconds
  RETURNS: NONE
--------------------------------------------------------------------------*/
void delay_ms(unsigned int n)
{
  systick = 0;
  while (systick &lt; n/10) nop();
  systick = 0;
}

/*--------------------------------------------------------------------------
  FUNC: 7/23/10 - Blinks LED in the doorbell
  PARAMS
    1: # of times to blink
    2: Duration in milliseconds
  RETURNS: NONE
  NOTES: This is a blocking function.  When the LED blinks, the only other code
    that can execute is in interrupt handlers.
--------------------------------------------------------------------------*/
void blink_LED(unsigned char times, unsigned int duration_ms)
{
  //while (times--)
  for (unsigned char i=times; i&gt;0; i--)
  {
    LED_PORT |= LED0;
    delay_ms(duration_ms);
    LED_PORT &amp;= ~(LED0);
    if (i &gt;= 1) delay_ms(duration_ms);
    else systick=0;
  }
}

/*--------------------------------------------------------------------------
  FUNC: 7/23/10 - Inits the entry code variables
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void initialize_entry_code(void)
{
  for (unsigned char i=0; i&lt;4; i++)
  {
    entry_code[i] = 0;
  }
  entry_index = 0;
}

/*--------------------------------------------------------------------------
  FUNC: 7/23/10 - Resets the system
  PARAMS: NONE
  RETURNS: NONE
  NOTES: Sets state to rest, sets code array to all 0, blinks the LED quickly
--------------------------------------------------------------------------*/
unsigned char system_reset(unsigned char state)
{
  initialize_entry_code();
  blink_LED(12,100);
  return STATE_REST;
}

/*--------------------------------------------------------------------------
  FUNC: 7/23/10 - Displays a 4 digit code in a series of LED blinks
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void readback(void)
{
  for (unsigned char i=0; i&lt;4; i++)
  {
    blink_LED(entry_code[i],200);
    delay_ms(1500);
  }
}

/*--------------------------------------------------------------------------
  FUNC: 8/12/10 - Used to set new code entrys or check codes for access
  PARAMS: Program state - STATE_PROGRAM (used to set new code)
  RETURNS: NONE
  NOTE: Added a new function call (and new function) store_code() to make
    this more device independent.
--------------------------------------------------------------------------*/
void check_code(unsigned char state)
{
  if (state == STATE_PROGRAM)
  {
    store_code();		//Write the new code to Info Flash memory
    get_code_from_flash();	//Load the newly stored code
    readback();			//Display the new code
  }
  //Verify the code entered is correct
  else if (entry_code[0]==code1 &amp;&amp; entry_code[1]==code2 &amp;&amp; entry_code[2]==code3 &amp;&amp; entry_code[3]==code4)
  {
    //Cycle to load to open the door
    LOAD_PORT |= LOAD0;
    delay_ms(800);
    LOAD_PORT &amp;= ~(LOAD0);
  }
  else state = system_reset(state);
}

/*--------------------------------------------------------------------------
  FUNC: 8/12/10 - Loads code from flash memory into code(x) variables
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void get_code_from_flash(void)
{
  char *p;
  p = (char *)0x1001;
  code1 = *p++;
  code2 = *p++;
  code3 = *p++;
  code4 = *p;
}

/*--------------------------------------------------------------------------
  FUNC: 8/12/10 - Saves newly entered code to info flash memory
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void store_code(void)
{
  FCTL2 = FWKEY + FSSEL1 + FN1;		// MCLK/3 for Flash Timing Generator
  clear_flash();			// Block must always be cleared before writing

  char *Flash_ptr;
  Flash_ptr = (char *) 0x1001;		//Point to Info Flash segment D

  FCTL3 = FWKEY;                            // Clear Lock bit
  FCTL1 = FWKEY + WRT;                      // Set WRT bit for write operation

  *Flash_ptr++ = entry_code[0];
  *Flash_ptr++ = entry_code[1];
  *Flash_ptr++ = entry_code[2];
  *Flash_ptr++ = entry_code[3];

  FCTL1 = FWKEY;                            // Clear WRT bit
  FCTL3 = FWKEY + LOCK;                     // Set LOCK bit
}

/*--------------------------------------------------------------------------
  FUNC: 8/12/10 - Clears one block of info flash memory
  PARAMS: NONE
  RETURNS: NONE
  NOTE: Info flash must be cleared before writing new values
--------------------------------------------------------------------------*/
void clear_flash(void)
{
  int *Flash_ptr;
  Flash_ptr = (int *)0x1000;	//Point to Info Flash segment D

  FCTL3 = FWKEY;		// Clear Lock bit
  FCTL1 = FWKEY + ERASE;	// Set Erase bit
  *Flash_ptr = 0;		// Dummy write to erase Flash segment D
  FCTL1 = FWKEY;		// Clear WRT bit
  FCTL3 = FWKEY + LOCK;		// Set LOCK bit
}

/*--------------------------------------------------------------------------
  FUNC: 7/23/10 - Main
--------------------------------------------------------------------------*/
int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  init_timers();
  init_io();
  get_code_from_flash();

  unsigned char state = STATE_REST;
  unsigned char system_timeout = 0;

  system_reset(state);

  for (;;) {
    switch(state){
      case STATE_REST :
        if( get_key_press( KEY0 )) {
          //Set entry index and array data to zero
          initialize_entry_code();
          //Set state to STATE_ENTRY
          state = STATE_ENTRY;
          //blink LED once (also resets systick)
          blink_LED(1,600);
          systick=0;
        }
        else if (get_key_press( KEY1 ))
        {
          //Programming button has been pushed
          state = STATE_PROGRAM_WAIT;
          system_timeout = 0;
          initialize_entry_code();
        }
      break;

      case STATE_PROGRAM_WAIT:
        //Waiting for code entry.
        if( get_key_press( KEY0 )) {
	  state = STATE_PROGRAM;
	  blink_LED(1,600);	//signal that we're ready for first entry
	  systick=0;
	}
	//Flash until user button is pushed
	else if ( systick &gt; 50 ) {
	  LED_PORT ^= LED0;
	  systick = 0;
	  //We've been idle for 2 minutes so reset the system.
	  if (++system_timeout &gt; 240) state = system_reset(state);
	}
      break;

      case STATE_PROGRAM:
      case STATE_ENTRY:
	//Has entry timed out?
	if (systick &gt; 150) {
	  //Make sure a number was entered
	  if(entry_code[entry_index]) {
	    //check if that was the final digit
	    if(++entry_index&gt;3)	{
	      check_code(state);
	      state = STATE_REST;
	    }
	    //Confirm with a blink (resets systick)
	    else blink_LED(1,600);
	  }
	  //current code is 0 which is not allowed, reset the system
	  else {
	    state = system_reset(state);
	  }
      	}
      	else if( get_key_press( KEY0 )) {
	  //increment digit to array
	  ++entry_code[entry_index];
	  //reset systick because button was just pushed
	  systick = 0;
      	}
      break;
    }
  }
}

//--------------------------------------------------------------------------
interrupt(TIMERA0_VECTOR) Timer_A (void)	// every 10ms
{
  static unsigned char ct0, ct1;
  unsigned char i;

  //TAR0 = (unsigned char)(signed short)-((F_CPU * .01) + 0.5);   // preload for 10ms

  i = key_state ^ ~KEY_PIN;    // key changed ?
  ct0 = ~( ct0 &amp; i );          // reset or count ct0
  ct1 = ct0 ^ (ct1 &amp; i);       // reset or count ct1
  i &amp;= ct0 &amp; ct1;              // count until roll over ?
  key_state ^= i;              // then toggle debounced state
  key_press |= key_state &amp; i;  // 0-&gt;1: key press detect

  ++systick;
}

10 thoughts on “Porting AVR code for MSP430 chips

  1. Pingback: Porting code to MSP430 - Hack a Day

  2. Problem with TinyMCE under Chrome, I’m thinking — the ‘mce-paste’ class suggests that TinyMCE was involved, but I’ve never seen that happen with MCE under Firefox or even IE, so I’m thinking it’s a little of both.

    Thanks for fixing it! It’s not so much that I need to port AVR code, but I bought some Launchpads as a learning tool to try to do something about my complete lack of microcontroller hacking skills, and I’ve been desperate to find good code examples that show how to use the MSP’s features. (And I suck at picking through microcontroller manuals and figuring out how to translate assembly-language code samples into C, apparently.) This is awesomely helpful, thanks again!

  3. Pingback: United States designers create a 23 cm tall heels | High-heeled Shoes

  4. Thank you so much for posting this project. I’m teaching myself the MSP430 and this was a good starter project to wrap my head around. You do a great job of formatting your work so it is very easy to follow.

    I was intrigued by two of your design choices.
    1. The fact that you did not use an interrupt driven model. I know you stated that you wanted everything to fit under 1k in the AVR version but it seemed to me like interrupts should do ok.
    2. The fact that the MSP version did not use low power modes. To me that the whole point of a MSP is that it makes it so easy to use interrupts and Low Power.

    So I borrowed your code and rewrote it to be more interrupt driven and go to Low Power. The whole thing is a little less than 800bytes with an optimized compile.

    In addition I decided to tweak things a bit to allow a compile time option to make the combination code any length instead of an arbitrary 4 digits.

    Here it is: ( I Hope the formatting works )
    #include
    #include
    //#define debug

    #define WAIT_TIME 2 // seconds between btn pushes; must be between 1 & 42
    #define CSTART 0×1000 // Memory location for the start of the code storage

    #define RLED BIT0
    #define GLED BIT6
    #define BTN BIT3
    #define PROG_BTN BIT4

    #define SLOW_BLINK 0xffff
    #define FAST_BLINK 0x1fff

    /*
    * State Machine Aliases
    * Set CODE_SIZE to however many numbers you want for your code
    * If you set it to three then the code will be three numbers each
    * separated by a pause
    * e.g.
    *
    *
    * If the code is right then the door will open
    * Otherwise you will see nothing unless in debug mode
    */
    #define CODE_SIZE 2
    #define STATE_REST CODE_SIZE
    unsigned char prog_mode = 0;
    unsigned char system_state = STATE_REST;

    // Global Vars
    unsigned char entry_code[CODE_SIZE];
    unsigned char stored_code[CODE_SIZE];

    // Prototypes
    void reset_state(void);
    void advance_state(void);
    void open_door(void);
    void delay(unsigned int d);
    void blink_LED(unsigned int times, unsigned int duration);
    void store_code(void);
    void get_code_from_flash(void);
    unsigned int check_code(void);
    #ifdef debug
    void display_code(unsigned char *c);
    void clear_flash(void);
    void blink_GLED(unsigned int times, unsigned int duration);
    #endif

    /*
    * Button press interrupt service routine
    */
    interrupt(PORT1_VECTOR) P1_ISR(void) {
    TACTL |= TACLR;
    delay(0×300); // debounce

    // See if the button or programming button were pushed
    if( (P1IFG & BTN) | (P1IFG & PROG_BTN) ) {
    // Set prog mode if Programming button was pushed
    if (P1IFG & PROG_BTN) {
    #ifdef debug
    blink_GLED(0×007,FAST_BLINK);
    #endif
    system_state = STATE_REST;
    prog_mode = 1;
    }

    // Handle normal button push
    if (system_state == STATE_REST) {
    blink_LED(5,FAST_BLINK); //show user we are ready
    advance_state();
    } else {
    entry_code[system_state]++; // Increment code then go back to sleep
    }
    // User now has WAIT_TIME seconds to do something before system reset
    // Get out of LPM4 so timers will work
    _BIC_SR_IRQ(LPM4_bits);
    _BIS_SR_IRQ(LPM3_bits);
    TACTL |= TACLR;
    }
    P1IFG = 0; // clear all interrupt flags
    }

    /*
    * This interrupt will fire if the user hasn’t pressed a button in
    * WAIT_TIME seconds
    */
    interrupt(TIMERA0_VECTOR) TA0_ISR(void) {
    switch(system_state) {
    case(STATE_REST)://This should never occur due to LPM4 disabling timers
    break;
    default:
    // Check code for zeros and reject them
    if (check_code()) {
    // Check if all numbers have been entered
    if (system_state == CODE_SIZE-1) {
    if (prog_mode) {
    #ifdef debug
    blink_GLED(0×2,FAST_BLINK);
    #endif
    store_code();
    prog_mode = 0;
    // Load new code
    get_code_from_flash();
    } else {
    open_door();
    }
    } else {
    blink_LED(1,SLOW_BLINK);
    advance_state();
    return;
    }
    }
    }
    // Since we didn’t advance_state() then that means all jobs are done
    // Go back into deep sleep and wait for a button press
    reset_state();
    }

    int main(void) {
    WDTCTL = WDTPW | WDTHOLD;

    // Normally I would set DCO to calibrated 1MHz
    // But we are not using it for timing so leave it as default
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
    BCSCTL3 |= LFXT1S1; // config low freq clock source to VLO

    // Setup procedure for input button and output LEDS
    P1DIR = RLED + GLED;
    P1REN = PROG_BTN + BTN; // enable pullup resistors buttons
    P1OUT = PROG_BTN + BTN; // set all pins low, set buttons to pullup
    P1IES |= PROG_BTN + BTN; // Set trigger on high to low event
    P1IFG &= ~PROG_BTN + ~BTN; // make sure flag is clear
    P1IE |= PROG_BTN + BTN; // enable interrupt

    // Setup Timer A to interrupt off of VLO every WAIT_TIME seconds
    TACCR0 = WAIT_TIME * 1500; // 1500 => ~12kHz VLO source divided by 8
    TACTL = TASSEL_1 + ID_3 + MC_1 + TACLR; // use ACLK, divide by 8, up-mode, and clear the count
    TACCTL0 = CCIE;

    get_code_from_flash();
    #ifdef debug
    display_code(stored_code);
    #endif

    // Unmask interrupts and Enter super duper low power mode
    // This will just wait for a btn press
    _BIS_SR(LPM4_bits + GIE);
    }

    /*
    * Sets system state to rest and enter LPM4
    */
    void reset_state(void) {
    system_state = STATE_REST;
    _BIC_SR_IRQ(LPM3_bits);
    _BIS_SR_IRQ(LPM4_bits);
    }

    void advance_state(void) {
    // Using Modulo is cleaner than if/else
    // but it adds 108 Bytes non-optimized ; 67 bytes optimized
    //system_state = (system_state + 1) % CODE_SIZE;
    // So do this instead
    system_state = system_state == STATE_REST ? 0 : system_state+1;
    entry_code[system_state] = 0;
    }

    /*
    * Check Code
    * This just looks to see if the user didn’t push
    * the button even once between WAIT_TIMEs.
    * If no presses then we will notify the user, reset the system
    * and go back to sleep.
    */
    unsigned int check_code(void) {
    if (entry_code[system_state] == 0) {
    blink_LED(25,FAST_BLINK); //tell user there is an error
    return 0;
    }
    return 1;
    }

    /*
    * Open the Door; All this does is flash the green LED for a while
    * In production this would trigger the relay and open the door
    */
    void open_door(void) {
    unsigned char test = 1;

    // Test user entered code against code from flash storage
    for (int i=0; i<CODE_SIZE; i++) {
    if (entry_code[i] != stored_code[i]) {
    test = 0;
    }
    }
    if (test) { // If codes match then open the door
    P1OUT |= GLED;
    delay(SLOW_BLINK);
    delay(SLOW_BLINK);
    delay(SLOW_BLINK);
    P1OUT &= ~GLED;
    }
    #ifdef debug
    else { // If codes don't match display the entered code
    display_code(entry_code);
    }
    #endif
    }

    /*
    * Blink Red LED
    */
    void blink_LED(unsigned int times, unsigned int duration) {
    for (unsigned int i = 0; i < times ; i++) {
    P1OUT |= RLED;
    delay(duration);
    P1OUT &= ~RLED;
    if (i < times) delay(duration);
    }
    }

    #ifdef debug
    /*
    * Blink Green LED
    */
    void blink_GLED(unsigned int times, unsigned int duration) {
    for (unsigned int i = 0; i < times ; i++) {
    P1OUT |= GLED;
    delay(duration);
    P1OUT &= ~GLED;
    if (i < times) delay(duration);
    }
    }
    #endif

    /*
    * Delay function.
    */
    void delay(unsigned int d) {
    unsigned int i;
    for (i = 0; i<d; i++) {
    nop();
    }
    }

    /*
    * Read Info Flash and retrieve the key code
    */
    void get_code_from_flash(void) {
    //FCTL2 = FWKEY + FSSEL1 + FN1; // set flash clock to MCLK / 3
    char *p;
    p = (char *)CSTART;
    for(int i=0; i<CODE_SIZE; i++) {
    stored_code[i] = *p++;
    }
    }

    /*
    * Store newly entered code into flash
    */
    void store_code(void) {
    volatile char *Flash_ptr;
    Flash_ptr = (char *)CSTART;

    FCTL2 = FWKEY + FSSEL_1 + FN1; // set flash clock to MCLK / 3
    delay(SLOW_BLINK); // Not sure why but code is more stable after
    // letting the clock settle

    /*
    * Erase one block of info flash
    */
    FCTL3 = FWKEY; // Clear lock bit
    FCTL1 = FWKEY + ERASE; //Set erase bit
    *Flash_ptr = 0; // Dummy write to erase flash

    /*
    * Write code to flash
    */
    FCTL1 = FWKEY + WRT; // Set WRT bit for write operation
    for (int i=0; i<CODE_SIZE; i++) {
    *Flash_ptr++ = entry_code[i];
    }
    FCTL1 = FWKEY; // Clear WRT bit
    FCTL3 = FWKEY + LOCK; // Set LOCK bit
    }

    #ifdef debug
    /*
    * Display Code
    * Let the user know the code when we are in debug mode
    * Called at Power up
    * Also called if the user entered an invalid code.
    * Format is:
    *
    *
    *
    * etc.
    */
    void display_code(unsigned char * c) {
    for (int i=0; i<CODE_SIZE; i++) {
    blink_LED(0×0003,FAST_BLINK);
    delay(SLOW_BLINK);
    blink_LED(c[i],SLOW_BLINK);
    }
    }
    #endif

  5. Pingback: Embedded Workshop » Blog Archiv » April 2011 Workshop on TI MSP430, part 1

  6. Pingback: download site

Leave a Reply