I had a FRAM experimenter’s board that I lent to a student for use with mspgcc4 and mspdebug. After a few hours of work, my student informed me that the board quit working and mspdebug presented an error about the fuse being blown.
It seemed improbable that the fuse had become blown (which you do by carefully flashing specific values to special addresses in the flash memory), since we were not experimenting with anything remotely related to fuse setting. A few Google searches turned up this thread, http://www.mail-archive.com/mspgcc-users@lists.sourceforge.net/msg10446.html,and TI tech support suggested that I use the Boot-Strap Loader (BSL) to reset the chip http://www.ti.com/lit/ug/slau319a/slau319a.pdf
Since I’ve worked through UART communications with the Launchpad at MSP430 Launchpad, it seemed obvious that the way to recover my FRAM board is to program a Launchpad to access the Flash BSL (detailed in Chapter 3 of the pdf) and give the Mass Erase command.
#include <msp430g2211.h> /* FRAM experimenter Mass Erase application. Invokes the FRAM * boot-strap loader and gives the the Mass Erase command via * 9600 bps UART communication, 8 bit data, even parity, 1 stop bit. * This code requires an external clock crystal for calibration * of 4 Mhz clock speed, unless you change HAVE_CRYSTAL below. */ #define RED_LED BIT0 #define GRN_LED BIT6 #define HAVE_CRYSTAL 1 // Or 0 if you don't have a clock crystal. #define FRAM_RST BIT4 // Connect Launchpad 1.4 to FRAM RST pin. #define FRAM_TEST BIT5 // Connect Launchpad 1.5 to FRAM TEST pin. #define TXD BIT1 // Connect Launchpad 1.1 to FRAM RXD pin. #define RXD BIT2 // Connect Launchpad 1.2 to FRAM TXD pin. /* Ticks per bit, and ticks per half. Use the following values * based on speed: 9600 bps -> 52 */ #define TPB 52 #define TPH TPB/2 volatile unsigned int parity = 0; volatile unsigned int TXWord = 0; volatile unsigned int RXWord = 0; volatile unsigned int rxbitcnt = 0; volatile unsigned int txbitcnt = 0; /* circular buffers for characters received/to send */ #define BSIZE 16 // must be power of 2 volatile unsigned char send_buffer[BSIZE]; volatile unsigned char recv_buffer[BSIZE]; volatile unsigned int sbhead=0, sbtail=0, rbhead=0, rbtail=0, bytestosend=0, bytesreceived=0; /* function prototypes */ unsigned char set_dco_c(unsigned int delta); void initUart( void ); inline void RX_Start( void ); unsigned char RX_Byte( void ); void TX_Byte( unsigned char c ); /* A crude delay function. Tune by changing the constant. */ inline void delay( unsigned int n ) { volatile unsigned int i = n<<2; while( i-- ) ; } void main(void) { int i, n=32; unsigned char erase[] = { 0x80, 0x01, 0x00, 0x15, /* command */ 0x64, 0xA3 /* checksum */ }; unsigned char c; /* stop the watchdog timer */ WDTCTL = WDTPW + WDTHOLD; /* Reset and Test pins start low */ P1OUT &= ~ (FRAM_RST + FRAM_TEST); P1DIR |= FRAM_RST + FRAM_TEST; delay(n); /* LEDs off, but we can use them for debugging if we want */ P1DIR |= RED_LED+GRN_LED; P1OUT &= ~ (RED_LED + GRN_LED ); /* set clock speed and initialize the timers and data pins */ initUart(); /* Toggle Test pin. */ P1OUT |= FRAM_TEST; delay(n); P1OUT &= ~FRAM_TEST; delay(n); /* Transition Test then Reset in order */ P1OUT |= FRAM_TEST; delay(n); P1OUT |= FRAM_RST; delay(n); P1OUT &= ~FRAM_TEST; // final transition starts the BSL delay(1500); /* Start listening for data, and enable interrupts, 9600 8E1. */ RX_Start(); /**** Serial is listening from here forward. ****/ __bis_SR_register( GIE ); /* Send the Mass Erase command. */ P1OUT |= RED_LED; for( i = 0; i < sizeof( erase ); i++ ) { TX_Byte( erase[i] ); } /* Wait for acknowledgement. */ while( ! bytesreceived ) ; P1OUT |= GRN_LED; c = RX_Byte(); if( c == 0x00 ) { // Response of 0x00 is success P1OUT &= ~RED_LED; } for( ; ; ) { /* go to sleep and wait for data */ __bis_SR_register( LPM0_bits + GIE ); } } /* Pass a delta value to set the DCO speed. Values for delta: * 244 -> 1 MHz 1220 -> 5 Mhz 2197 -> 9 Mhz 3173 -> 13 Mhz * 488 -> 2 Mhz 1464 -> 6 Mhz 2441 -> 10 Mhz 3417 -> 14 Mhz * 732 -> 3 Mhz 1708 -> 7 Mhz 2685 -> 11 Mhz 3662 -> 15 Mhz * 976 -> 4 Mhz 1953 -> 8 Mhz 2929 -> 12 Mhz 3906 -> 16 Mhz * General formula: * floor(x hz / 4096) -> delta * Return values: * 0 - DCO set * 255 - Timeout * Adapted from code by J. B. Remnant. */ unsigned char set_dco_c(unsigned int delta) { unsigned int Compare, Oldcapture = 0; int direction=0; volatile int i; unsigned char signchg=0; /* set auxiliary clock to use /8 divider (requires external crystal) */ BCSCTL1 |= DIVA_3; /* Timer A0 capture positive edge, select input 1, capture mode */ TACCTL0 = CM_1 + CCIS_1 + CAP; /* Timer A use submain clock, continuous up mode, clear timer */ TACTL = TASSEL_2 + MC_2 + TACLR; // SMCLK, cont-mode, clear /* loop 10000 times to set clock */ for ( i = 10000; i && (signchg<3); i-- ) { while (!(CCIFG & TACCTL0)) ; // Wait until capture occurred TACCTL0 &= ~CCIFG; // Capture occurred, clear flag Compare = TACCR0; // Get current captured SMCLK Compare = Compare - Oldcapture; // SMCLK difference Oldcapture = TACCR0; // Save current captured SMCLK if (delta == Compare) break; // If equal, leave the loop else if (delta < Compare) { DCOCTL--; // DCO is too fast, slow it down if (DCOCTL == 0xFF) // Did DCO roll under? if (BCSCTL1 & 0x0f) BCSCTL1--; // Select lower RSEL if( direction > 0 ) { // catch for successive direction changes signchg++; // and increment count when one happens } else { signchg=0; } direction = -1; } else { DCOCTL++; // DCO is too slow, speed it up if (DCOCTL == 0x00) // Did DCO roll over? if ((BCSCTL1 & 0x0f) != 0x0f) BCSCTL1++; // Sel higher RSEL if( direction < 0 ) { // catch for successive direction changes signchg++; // and increment count when one happens } else { signchg=0; } direction = +1; } } TACCTL0 = 0; // Stop TACCR0 TACTL = 0; // Stop Timer_A BCSCTL1 &= ~DIVA_3; // ACLK = LFXT1CLK /* i>0 means that DCO is set correctly -- set return value accordingly */ Compare = (i) ? 0 : 255; /* delay loop */ for (i = 0; i < 0x4000; i++) ; return Compare; } void initUart( void ) { /* Set clock speed to 4 Mhz, no divider. */ #if HAVE_CRYSTAL set_dco_c( 976 ); #else /* If no crystal, this setting will probably work. */ BCSCTL1 = CALBC1_1MHZ + 4; DCOCTL = CALDCO_1MHZ; #endif BCSCTL2 &= ~(DIVS_3); /* Set timer A to use continuous mode 4 Mhz / 8 = 500 khz. */ TACTL = TASSEL_2 + MC_2 + ID_3; /* When TXD isn't being used, it should be set to binary 1. */ TACCTL0 = OUT; /* TXD and RXD set for timer function, RXD input, TXD output */ P1SEL |= TXD + RXD; P1DIR &= ~ RXD; P1DIR |= TXD; } /* This continuously sends bits of the TXWord starting from the * least significant bit (the 0 start bit). One bit is sent every * time the handler is activated. When the bits run out, a new * byte is loaded from the data pointer, until bytestosend equals 0. */ void TimerA0 (void) __attribute__((interrupt(TIMERA0_VECTOR))); void TimerA0(void) { int i; if( txbitcnt ) { /* Send least significant bit, by changing output mode to * Reset (OUTMOD0|OUTMOD2) for binary 0 and Set (OUTMOD0) for 1. */ if( TXWord & 0x01 ) { TACCTL0 &= ~OUTMOD2; } else { TACCTL0 |= OUTMOD2; } TXWord >>= 1; txbitcnt --; } /* If there are no bits left, load the next byte */ if( !txbitcnt ) { if( bytestosend ) { TXWord = send_buffer[sbtail++]; sbtail &= BSIZE-1; bytestosend --; /* Get the parity bit to make even parity. */ parity = 0; for( i = 0; i < 8; i++ ) { parity = ( parity ^ (TXWord>>i)); } parity &= 1; /* Load next byte with even parity, 1 stop bit 0x200, and shifted * left to make the start bit. */ TXWord = ( 0x200 | (parity<<8) | TXWord ) << 1; /* 1 start bit + 8 data bits + 1 parity + 1 stop bit */ txbitcnt = 11; } else { /* turn off interrupts if not receiving */ if( ! rxbitcnt ) TACCTL0 &= ~ CCIE; } } /* add ticks per bit to trigger again on next bit in stream */ CCR0 += TPB; /* reset the interrupt flag */ TACCTL0 &= ~CCIFG; } void RX_Start( void ) { /* Make ready to receive character. Synchronize, negative edge * capture, enable interrupts. */ TACCTL1 = SCS + OUTMOD0 + CM1 + CAP + CCIE; } /* Stuffs a byte into the buffer, and schedules it to be sent. */ void TX_Byte( unsigned char c ) { if( bytestosend < BSIZE ) { send_buffer[sbhead++] = c; sbhead &= BSIZE-1; bytestosend ++; } /* Turn on transmitting if needed. */ if( ! (TACCTL0 & CCIE)) { /* Start sending after 1 more bit of mark time. */ CCR0 = TAR + TPB; /* Transmit 1/Mark (OUTMOD0) and enable interrupts */ TACCTL0 = CCIS0 + OUTMOD0 + CCIE; } } /* Retrieves a byte from the receive buffer. */ unsigned char RX_Byte( void ) { unsigned char c = 0; if( bytesreceived ) { c = recv_buffer[rbtail++]; rbtail &= BSIZE-1; bytesreceived --; } return c; } void TimerA1 (void) __attribute__((interrupt(TIMERA1_VECTOR))); void TimerA1(void) { /* If we just caught the 0 start bit, then turn off capture * mode (it'll be all compares from here forward) and add * ticks-per-half so we'll catch signals in the middle of * each bit. */ if( TACCTL1 & CAP ) { /* 9 bits pending = 8 bits + 1 parity */ rxbitcnt = 9; RXWord = 0; /* next interrupt in 1.5 bits (i.e. in middle of next bit) */ CCR1 += TPH + TPB; /* reset capture mode and interrupt flag */ TACCTL1 &= ~ ( CAP + CCIFG ); return; } /* Otherwise we need to catch another bit. We'll shift right * the currently received data, and add new bits on the left. */ RXWord >>= 1; if( TACCTL1 & SCCI ) { RXWord |= 0x100; } rxbitcnt --; /* last bit received */ if( ! rxbitcnt ) { /* Record this byte and reset for next. * Put character in circular recv_buffer (unless full). */ if( bytesreceived < BSIZE ) { recv_buffer[rbhead++] = (unsigned char)RXWord; rbhead &= BSIZE-1; bytesreceived ++; } /* we're done, reset to capture */ TACCTL1 = SCS + OUTMOD0 + CM1 + CAP + CCIE; return; } /* add ticks per bit to trigger again on next bit in stream */ CCR1 += TPB; /* reset the interrupt flag */ TACCTL1 &= ~CCIFG; }
This code was designed for a Launchpad board that has the clock crystal soldered in place (since it uses that clock to calibrate the main system clock to 4Mhz instead of the default 1Mhz speed). But if you don’t have a crystal, you can change the value of HAVE_CRYSTAL to 0, and the program will try a "best guess" that will probably work.
On your FRAM board, remove the jumpers from TXD, RXD, RST, and TEST. You’re going to connect the "chip side" of these jumpers to your Launchpad board. On your Launchpad board, remove the TXD and RXD jumpers.
These are the connections to make between the boards:
-
FRAM TXD to Launchpad RXD (P1.2)
-
FRAM RXD to Launchpad TXD (P1.1)
-
FRAM RST to Launchpad P1.4
-
FRAM TEST to Launchpad P1.5
-
FRAM Vcc to Launchpad Vcc
-
FRAM GND to Launchpad GND
Connect the USB cable from your computer to your Launchpad card, and load the program.
$ msp430-gcc -O2 -mmcu=msp430g2211 -o fram_bsl.elf fram_bsl.c $ mspdebug rf2500 "prog fram_bsl.elf"
When the program runs on your Launchpad, you should briefly see the red LED come on. If it was successful, the red LED will go dark and the green LED will remain lit. At that point, your FRAM board should be erased and ready to use again (don’t forget to re-install the jumpers on both boards).
I was asked by someone to provide an elf file, since different compilers can sometimes want slightly different source and syntax. I have generated one and uploaded it. This one is compiled with HAVE_CRYSTAL defined to 0, so it should work on a Launchpad with or without the clock crystal. I believe it will work fine on an msp430g2211, an msp430g2231, or an msp430f2012; perhaps others as well.
Download: fram_bsl.elf
Good luck.