This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Writing to Flash on MSP430G2553

Other Parts Discussed in Thread: MSP430G2553

I am having problems writing to memory after erasing a segment.  So far I have only developed a simple test code. I erase the segment starting with address 0xC400. Then I write a test byte to that address.  Also I write a test byte to address 0xC600. When I run the code and read it back out of the microcontroller, address 0xC400 is blank, but the test byte is there at 0xC600. I have also run tests where I did an erase of memory starting with address 0XC600.  When I read it back then the test byte at 0xC400 is there, but the one at 0xC600 was not.  I also ran it erasing the segment starting at 0XC000.  This of course erased all of the program, but it proved my erase code works.  So it appears my code will erase a segment and can program a byte(or word, I have done it that way as well), but can not write to a segment that it erased. Any suggestions where I may have gone wrong? 

int main(void)
{
     BCSCTL1 = CALBC1_1MHZ;
     DCOCTL = CALDCO_1MHZ;

     char *Flash_ptr = (char *)0xC400;
     char *data_ptr = (char *)0xC600;

     __bic_SR_register (GIE); //disable interrupts

     WDTCTL = WDTPW + WDTHOLD; // stop watchdog timer

     P3DIR = 0x08; //setup output for LED
     P3OUT = 0x00;

     FCTL3 = FWKEY;
     FCTL1 = FWKEY + ERASE;

     *Flash_ptr = 0; // erase Flash segment
     while(FCTL3 & BUSY);

     FCTL1 = FWKEY+WRT;

     *Flash_ptr = 0xAA;
     *data_ptr = 0x55;

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

     P3OUT = 0x08; //turn on LED

     for (;;) // loop forever
     {}
}

  • OK I fixed the problem. 

    I added             

         FCTL2 = FWKEY + FSSEL_1 + FN0;

    to the erase sequence and it now works.  Most of the code examples I read did not have this line.    FCTL2 must have been set to the default value which I believe would have been FCTL2 = FWKEY + FSSEL_1 + FN2 which would have given me a clock divider of 3. Since my system clock, if I understand this correctly, was 1Mhz, this would have given me a programming frequency of 333KHz, which should have been well within the acceptable range.  But with the configuration that works, I am using a frequency of 1MHz, which should not work.  So I now have code that works, but I do not understand why.  Can anyone see where I made the mistake?   

  • I am still having a variation of the same problem as before.  I have modified to code to write to multiple addresses. Only the first address in the erased segment is successfully written to.   But all addresses in the segment my code does not erase are successfully written to.  Is there some timing issue that I have overlooked?  

    int main(void) 

    {
         BCSCTL1 = CALBC1_1MHZ;
         DCOCTL = CALDCO_1MHZ;

         char block[32] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
         char *Flash_ptr = (char *)0xC400;
         char *data_ptr = (char *)0xC600;
         int j = 0;

         WDTCTL = WDTPW + WDTHOLD; // stop watchdog timer
         __bic_SR_register (GIE); //disable interrupts

         P3DIR = 0x18; //setup output for LED
         P3OUT = 0x00;

         while(FCTL3 & BUSY);

         FCTL2 = FWKEY + FSSEL_1 + FN0;
         FCTL3 = FWKEY;
         FCTL1 = FWKEY + ERASE;

         *Flash_ptr = 0; // erase Flash segment

          while(FCTL3 & BUSY);

         FCTL1 = FWKEY+WRT;

         for(j = 0; j < 32; j++)
         {
              *Flash_ptr = block[j];
              Flash_ptr++;
              while(FCTL3 & BUSY);
         }

         for(j = 0; j < 32; j++)
         {
             *data_ptr = block[j];
              data_ptr++;
              while(FCTL3 & BUSY);
          }

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

         P3OUT = 0x18; //turn on LED

         for (;;) // loop forever
         {}
    }

    0xC400: 01 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF | ................
    0xC410: ------- b l a n k ----(all 0xFF)-------


    0xC600: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 | ................
    0xC610: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 | ................
    0xC620: ------- b l a n k ----(all 0xFF)-------

  • Hi Alan!

    For me your code seems to be OK, so I tried it - here is the result:

    Works fine! @0xC400 got 32 bytes of 01 and @0xC600 also.

  • Thanks.  I am going to try and do some more scope work and see if I can find an electrical problem.  But from what I have seen so far, the voltage looks to be a nice solid 3.25V. 

  • Did you run that in a simulation, or on actual hardware?  I am using MSPGCC and MSPDEBUG running under linux.  In simulation the new data is never written to memory.  But when I run it on actual hardware, I get the results I posted above. 

  • This was on real hardware. LaunchPad with MSP430G2553 under CCS V6.

  • Although this is working on my MSP, the frequency for the flash controller is too high, isn't it? The maximum is 476kHz. With a system speed of 1MHz setting of FN0 only is not enough, as it results in 500kHz.

    Dennis

  • That is what I was asking before.  This should not work, but it does.  If I try other speeds, then I can not get it to write at all.  I wonder if this could be a problem with the GCCMSP compiler.  Thank you for your help, at least you have confirmed my concept is not flawed. 

  • It will work, the given specs in the datasheet are the values at which TI guarantees the device will function properly. 500kHz is only 24kH above, that could, but must not cause problems. Of course you should never go out of specs! So don't do it. I have (just for fun) overclocked an MSP to 25MHz which was specified to 16MHz and it worked well. But I would never do this in a product that will be sold to customers.

  • The code above would allow me erase memory or write to memory, but if I tried to write to a location in a segment that I had just erased, the results would be unpredictable.  Usually it would not write at all, and when it did only to the first memory location in the segment, well the first I tried to write to.  But when I followed this by a write in another segmant that I did not erase, with the code, it would write consistently every time.

    I changed

    FCTL2 = FWKEY + FSSEL_1 + FN0;  

    to   

    FCTL2 = 0xA540;

    which should give me the same value, my code writes perfectly under all conditions. I ended up with this value strictly by trial and error.  The datasheet says this should not work.  

    I did a little experiment on the side using timer A and 

    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;

    does give me a 1Mhz clock. 

    The testing I am doing on the hardware is telling me this does work.  I am just at a loss to understand why. 

  • Hi Alan!

    Your two expressions aren't the same!

    ( FWKEY | FSSEL_1 | FN0 ) is equal to 0xA541


    This is how I would configure it, without looking at your config now:

    void configure_clock_module( void )
    {
      BCSCTL1 = CALBC1_1MHZ; // Set range to 1MHz
      DCOCTL  = CALDCO_1MHZ; // Set DCO step and modulation to 1MHz
    }
    
    void configure_flash( void )
    {
      // Set password to enable writing
      // SMCLK as source
      // Division of 1MHz SMCLK by 3 (2 + 1) results in 333kHz clock
      FCTL2 = ( FWKEY | FSSEL_2 | FN1 );
    }

    And this is how I write to flash:

    void flash_write_byte( uint16_t address, uint8_t byte )
    {
      uint8_t *flash_ptr;
    
      flash_ptr = (uint8_t*) address;
      FCTL3 = FWKEY;                  // Clear lock-bit
      FCTL1 = (FWKEY | WRT);          // Set write-bit
      *flash_ptr = byte;              // Write byte - CPU hold
      FCTL1 = FWKEY;                  // Clear write-bit
      FCTL3 = (FWKEY | LOCK);         // Set lock-bit
    }

    And how to read from it:

    uint8_t flash_read_byte( uint16_t address )
    {
      uint8_t *flash_ptr;
    
      flash_ptr = (uint8_t*) address;
    
      return *flash_ptr;
    }

    For writing arrays:

    void flash_write_byte_array( uint16_t address, uint8_t *source_buffer, uint8_t source_buffer_start_address, uint8_t byte_count )
    {
      uint8_t counter;
    
      for( counter = 0; counter < byte_count; counter++ )
      {
        flash_write_byte( (address + counter), source_buffer[(source_buffer_start_address + counter)] );
      }
    }

    For reading an array:

    void flash_read_byte_array( uint16_t address, uint8_t *dest_buffer, uint8_t dest_buffer_start_address, uint8_t byte_count )
    {
      uint8_t counter;
    
      for( counter = 0; counter < byte_count; counter++ )
      {
        dest_buffer[(dest_buffer_start_address + counter)] = flash_read_byte( (address + counter) );
      }
    }

    And for deleting a segment:

    void flash_delete_segment( uint16_t segment )
    {
      uint8_t *flash_ptr;
    
      flash_ptr = (uint8_t*) segment;
      FCTL3 = FWKEY; // Clear lock-bit
      FCTL1 = (FWKEY | ERASE); // Set erase-bit
      *flash_ptr = 0; // Dummy-write to delete segment - CPU hold
      FCTL3 = (FWKEY | LOCK); // Set lock-bit
    }

    Where segment is the start-address of the segment, which is

    #define FLASH_INFO_SEG_A_START     0x10C0 // Do not write to segment A
    #define FLASH_INFO_SEG_A_END       0x10FF // Calibration-data stored here
    
    #define FLASH_INFO_SEG_B_START     0x1080
    #define FLASH_INFO_SEG_B_END       0x10BF
    
    #define FLASH_INFO_SEG_C_START     0x1040
    #define FLASH_INFO_SEG_C_END       0x107F
    
    #define FLASH_INFO_SEG_D_START     0x1000
    #define FLASH_INFO_SEG_D_END       0x103F

    for the G2955 for example.

    Dennis

  • Thanks Dennis

    I see where I misunderstood now.  

**Attention** This is a public forum