제품을 만들다 보면 어떻한 데이터를 저장해야 하는 경우가 발생합니다. 가령 제품이 전원이 꺼지기 전의 상태를 기억해야 하거나, 제품이 정확한 값을 출력 시키기 위해서 교정한 데이터를 기억하여 활용하는 상황이 발생하게 됩니다. 이런 경우 데이터의 저장 빈도나 제품의 목적 및 활용성에 따라 다양한 방법으로 데이터를 저장하게 됩니다. 일반적인 소규모의 임베디드 제품에선 저장해야 하는 데이터의 양도 적을 뿐더러 저장 빈도도 적기 때문에 주로 MCU 내부에 존재하는 저장 장소를 이용합니다. 하지만 모바일 제품으로 갈수록 저장 빈도도 많아지고 데이터의 크기도 많기 때문에 MCU 외부에 존재하는 저장 장솔르 이용합니다. 가령 저장량은 작지만 저장빈도가 높을 경우는 외부의 EEPROM 같은 걸 사용하고 용량이 커질수록 Flash 로 넘어 가게 됩니다.

하지만 우리가 지금 언급하려는 TI의 MSP430AFE253는 소규모 임베디드 시스템에 적용하는 MCU이면서 내부에 EEPROM 같은 별도의 저장 공간이 들어 있지 않습니다. 그러다 보니 TI에선 다른 방법을 활용하여 EEPROM 같은 기능을 제공하게 됩니다.

 

그렇다면 어떻게 하느냐? 여러 방법들이 존재 하겠지만 TI의 경우는 MCU의 프로그램이 구동되는 Flash 영역 일부를 특정 영역으로 지정하여 이 부분에는 사용자가 임으로 데이터를 기록 할 수 있도록 하였습니다. 그런데 Datasheet에서 이 부분을 언급하는 part에선 TI에서 지정한 영역 이외에도 프로그램이 하다고 설명을 합니다. 자세히 읽어 보면 이게 바로 일종의 bootloader를 만들 때 참고해야 할 부분으로 보여 집니다. 

아무튼, 지금 언급하려는 것은 그런 부분이 아니니 넘기겠습니다.

 

그렇다면 지금까지 설명한 부분이 무엇이냐?! 하시겠죠. 그부분이 바로 제목에서 보셨듯이 Information Layer 입니다. Information Layer의 설명은 MSP430 User's Guide 의 Flash Memory Controller에 존재합니다.(여기서 설명할 MCU는 MSP430AFE253을 기준으로 설명해 드리고 있으며 해당 문서의 번호는 SLAU144H 입니다.) Flash Memory Controller는 bit, byte, word 단위로 Address 접근과 프로그램이 가능합니다. 물론 MSP430의 Flash Memory Controller은 많은 장점을 가지고 있으나 지금 궁금한것은 이것이 아니니 넘기도록 하겠습니다.


MSP의 Flash Memory는 Information Layer 라는 부분을 따로 때어 놓았을 때부터 알아 보셨듯이 하나의 Flash Memory를 자잘하게 나누어 놓았습니다. 다음과 같이 크게 Flash Main Memory와 Flash Information Memory로 구분됨을 보실수 있으실 것입니다.



일단은 User's Guide에 있는 32KByte로 예로 들었습니다만, Flash Memory는 Segment로 구분이 되고 세그 먼트는 다시 Block으로 구분이 됨을 알 수 있습니다. 여기서 Information Layer은 Block 단위로는 지울수 없고, 오직 Segment 단위로만 삭제가 가능합니다. 그리고 Main Memory의 Block의 크기는 64byte이고 Segment의 크기는 256byte입니다.

Infomation Layer의 Segment 크기는 64byte인데 이것이 최소 삭제 가능 메모리의 크기입니다. 이것이 나타내는 다른 의미는 Segment A의 내용을 수정하기 위해선 최소한 64byte의 ram이 필요합니다. 이런 이유는 MSP430의 Flash Memory 동작 특성 때문에 쓰기는 Byte 나 Word 단위로 가능하지만 삭제는 최소 Block이나 Segment 단위로 가능하기 때문입니다. 이런 동작 특성은 프로그램을 작성할 때 Information Layer 수정시 반드시 읽기 먼저 수행하고 이후에 쓰기를 실시하게 하는 이유가 됩니다.


그렇다면 Information Layer에 프로그램을 작성하기 위해선 어떻게 해야 할까요? 여기에 대해선 TI가 참 친절하게도 Example Source을 제공하고 있습니다. 특별하게 Basic Clock Module를 변경하지 않으셨다면 다음의 예제를 참고 하시면 됩니다.



[MSP430AFE_FLASHWRITE_01‎‎‎‎]


#include <msp430afe253.h>


char value;  // 8-bit value to write to seg C


// Function prototypes

void write_SegC(char value);

void copy_C2D(void);


void main(void)

{

    WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer


    FCTL2 = FWKEY + FSSEL0 + FN1;             // MCLK/3 for Flash Timing Generator

    value = 0;                                // initialize value


    while(1)                                  // Repeat forever

    {

        write_SegC(value++);                    // Write segment C, increment value

        copy_C2D();                             // Copy segment C to D

        __no_operation();                       // SET BREAKPOINT HERE

    }

}


void write_SegC(char value)

{

    char *Flash_ptr;                          // Flash pointer

    unsigned int i;


    Flash_ptr = (char *)0x1040;               // Initialize Flash pointer

    FCTL3 = FWKEY;                            // Clear Lock bit

    FCTL1 = FWKEY + ERASE;                    // Set Erase bit

    *Flash_ptr = 0;                           // Dummy write to erase Flash seg


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


    for (i = 0; i < 64; i++)

    {

        *Flash_ptr++ = value;                   // Write value to flash

    }


    FCTL1 = FWKEY;                            // Clear WRT bit

    FCTL3 = FWKEY + LOCK;                     // Set LOCK bit

}


void copy_C2D(void)

{

    char *Flash_ptrC;                         // Segment C pointer

    char *Flash_ptrD;                         // Segment D pointer

    unsigned int i;


    Flash_ptrC = (char *)0x1040;              // Initialize Flash segment C ptr

    Flash_ptrD = (char *)0x1000;              // Initialize Flash segment D ptr

    FCTL3 = FWKEY;                            // Clear Lock bit

    FCTL1 = FWKEY + ERASE;                    // Set Erase bit

    *Flash_ptrD = 0;                          // Dummy write to erase Flash seg D

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


    for (i = 0; i < 64; i++)

    {

        *Flash_ptrD++ = *Flash_ptrC++;          // copy value segment C to seg D

    }


    FCTL1 = FWKEY;                            // Clear WRT bit

    FCTL3 = FWKEY + LOCK;                     // Set LOCK bit

}



하지만 만약 외부 클럭을 받거나 내부 클럭을 변경하였다면 그에 따라 설정도 바뀌게 되고, 이러한 클럭 변화는 당연하게도 MCU 전반에 영향을 미치게 됩니다. 이는 Flash Memory(Information Layer)도 예외는 아닙니다. 따라서 위와 같은 기본 예제의 소스를 활용해선 답이 나오지 않습니다. 그렇다면 어떻게 해야 하느냐? 이는 다시 User's Guide를 확인해 봐야 합니다. 일단 여기선 Basic Clock Module에 대한 설명은 제외 하도록 하겠습니다. User's Guide에 보면 'Initiating an Erase from Within Flash Memory', 'Initiating an Erase from RAM', 'Initiating a Byte/Word Write from Within Flash Memory', 'Initiating a Byte/Word Write from RAM', 'Block Write Flow and Example' 라는 항목이 존재합니다. 여기에 보면 바로 Flow-Chart가 나오는데, 이에 맞추어서 프로그램을 작성하시면 됩니다.
 
우선 Information Layer를 초기화 하기 위해선 Basic Clock Module의 영향을 어떻게 받았는지 알아야 합니다. 이는 FCTL2 레지스터의 설정이 변경됨을 의미합니다. 역시서 우리가 변경해야 할 것은 FNx의 부분인 5~0번 bit들 입니다. 그렇다면 어떻게 설정을 하느냐? 생각보다 간단합니다. User's Guide의 Flash Memory Timing Generator부분을 보면 256K <= f-FTG <=477가 되도록 FNx를 수정하라고 합니다. 만약 사용하는 Basic Clock이 MCLK(또는 SMCLK) = 14.745MHz 이고 FN을 36으로 설정한다고 한다면 그 값은 MCLK / (FNx + 1) 이므로 398.5153가 됩니다. 즉 f-FTG 범위내에 들어 가는 것이죠. 아무튼 다음의 공식에 따라 FNx를 구하시면 됩니다. [ FNx = (S)MCLK / f-FTG - 1 ]
 
그 다음으론 'Initiating an Erase from Within Flash Memory', 'Initiating an Erase from RAM', 'Initiating a Byte/Word Write from Within Flash Memory', 'Initiating a Byte/Word Write from RAM', 'Block Write Flow and Example' 등 다양한 것들이 있지만 사실 이것들은 어떻게 보면 'Block Write Flow and Example'의 일부분이라고 볼 수 있기에 이 부분만 설명 하도록 하겠습니다.
'Block Write Flow and Example'의 Flow-Chart는 다음과 같습니다.




여기서 중요한 것은 바로 Flash Memory Controller가 충분하게 Flash에 data를 기록할 수 있는 시간을 주는 것입니다. 만약 가장 기본적인 클럭(MSP430AFE253은 Basic Clock Module이 1MHz이다.)보다 낮으면 상관 없지만 만약 높다면 Debuger 상에선 Information Layer에 기록 된 것처럼 보이지만 리셋지 해당 영역이 초기화 되거나 엉뚱한 값이 저장되어 있는 것을 보실 수가 있습니다. 따라서 위의 Flow-Chart에서 BUSY와 WAIT를 명심하셔서 삽입하셔야 합니다. 이는 MSP430의 Information Layer 뿐만 아니라 SRAM, EEPROM 등도 마찬가지입니다. 해당 모듈들이 기존의 작업을 완료하지 못한 상태인데 외부에서 접근을 시도하게 되면 해당 접근 번지나 이전 접근 번지엔 원하지 않는 필요없는 데이터가 기록되게 됩니다.

이러한 BUSY와 WAIT를 고려하여 프로그램을 수정하게 되면 다음과 같습니다.



[MSP430AFE_FLASHWRITE_02‎]


//****************************************************************//

//                                                                //

//           MSP430 : Flash Memory (Informaion Area)              //

//                                                                //

//****************************************************************//



#define FLASH_WRITE_ENABLE     (1)

#define FLASH_SIZE             (0x30)


#define FLASH_FRKEY            (0x9600)  // FRKEY

#define FLASH_FWKEY            (0xA500)  // FWKEY


// FCTL3, This bit indicates the status of the flash timing generator

#define FlashBusy() (FCTL3 & BUSY == BUSY) ? true : false

// FCTL3, Indicates the flash memory is being written to.

#define FlashWait() (FCTL3 & WAIT == WAIT) ? true : false



unsigned char flash[FLASH_SIZE];



// Interrupt enable

void IntGlobal(bool bConfig){

    // Interrupt enable (power mode : normal)

    if( bConfig ) _EINT();

    // Interrupt disable (power mode : normal)

    else          _DINT();

}    




void FlashConfigSet(void){

    while( FlashBusy() );

    FCTL2 = FWKEY + FSSEL0 + FN5 + FN2;   MCLK/3 for Flash Timing Generator

    FCTL1 = FWKEY;

    FCTL3 = FWKEY;

}



void FlashPutEnable(void){

    unsigned char* address = (unsigned char *)0x1000;  

    

    while( FlashBusy() );  // Busy check

    FCTL3 = FLASH_FWKEY;  // Clear Lock bit

    FCTL1 = FLASH_FWKEY | ERASE;  // Set Erase bit

}

void FlashPutDisable(void){

    FCTL1 = FLASH_FWKEY; // Clear WRT bit

    FCTL3 = FLASH_FWKEY | LOCK;  // Set LOCK bit

}



void FlashCharGet(unsigned char *pucData, unsigned short usAddress){

    unsigned char* address = (unsigned char *)0x1000;  // Flash segment D ptr (This time is Initialize Flash segment D ptr)


    *pucData = *(address + usAddress);  // Ram to flash memory

}


void FlashRead(unsigned char *pucData, unsigned short usAddress, unsigned short usCount){

    unsigned char* address = (unsigned char *)0x1000;  // Flash segment D


    for( int i = 0; i < usCount ; i++ ){

        *(pucData + i) = *(address + usAddress + i);  // Ram to flash memory

    }

}



void FlashStringPut(unsigned char *pucData, unsigned short usAddress, unsigned short usCount){

    // Flash segment D ptr, Flash pointer, Initialize Flash pointer

    unsigned char* address = (unsigned char *)0x1000;      

    

    while( FlashBusy() );  // Busy check

    FCTL1 = FLASH_FWKEY | WRT;  // Set WRT bit for write operation    

        

    for( int i = 0; i < usCount ; i++ )

    {

        *(address + usAddress + i) = *(pucData+i);  // Data save

        while( FlashWait() );  // Wait check

    }    

    while( FlashBusy() );  // Busy check

}


void FlashCharWrite(unsigned char ucData, unsigned short usAddress){

    if( FLASH_WRITE_ENABLE )

    {

        IntGlobal(false);

    

        FlashRead( flash, 0, FLASH_SIZE );

        

        flash[usAddress] = ucData;

        

        FlashPutEnable();

        FlashStringPut( flash, 0, FLASH_SIZE);

        FlashPutDisable();

    

        IntGlobal(true);

    }

}


void FlashWrite(unsigned char *pucData, unsigned short usAddress, unsigned short usCount){

    if( FLASH_WRITE_ENABLE ){

        IntGlobal(false);

        

        FlashRead( flash, 0, FLASH_SIZE );

        

        for( int i = 0; i < usCount ; i++ ){

            flash[usAddress + i ] = *(pucData+i);          // Data save

        }

        

        FlashPutEnable();

        FlashStringPut( flash, 0, FLASH_SIZE);

        FlashPutDisable();

        

        IntGlobal(true);

    }

}





Posted by klisty
,