전원이 꺼지거나 리셋이 되도 지워지지 말아야할 정보를 사용할 경우, 

외부에 추가로 EEPROM 을 붙여서 SPI/I2C 로 읽고 쓸 수도 있지만,
STM32 MCU 에  Flash 메모리도 남고 아주 자주 쓰고 지우는 경우도 아니라서 Flash 메모리를 EEPROM 처럼 사용하는 방법을 알아보고 간단한 테스트를 해 보려고 합니다.

테스트하기 편하게 제가 갖고 있는 STM32F411 Nucleo 보드를 사용해서 STLINK 로 프로그램을 다운로드하고,
UART로 테스트 내용을 확인할 계획입니다. 추가로 외부에 연결할 부품도 필요 없기에 보드 사진은 생략합니다.

Nucleo 보드에서 사용하는 기능은 다음과 같습니다.
1. User SW 입력 (GPIO INPUT)
2. LED 출력 (GPIO OUT)
3. 디버그용 UART




내부 플레쉬 메모리를 읽고 쓰기 위해서 예전에 Custom DFU bootloader 를 만들 때 사용했던 함수들을 재활용해야겠습니다. 

메모리 엑세스 관련, 간단하게 함수 프로토 타입만 적어 놓겠고 자세한 내용은 예전 글을 참고 하시기 바랍니다.
1
2
3
4
5
6
7
8
uint16_t MEM_If_Init_FS(void);
uint16_t MEM_If_Erase_FS (uint32_t Add);
uint16_t MEM_If_Write_FS (uint8_t *src, uint8_t *dest, uint32_t Len);
uint8_t *MEM_If_Read_FS  (uint8_t *src, uint8_t *dest, uint32_t Len);
uint16_t MEM_If_DeInit_FS(void);
 
uint32_t GetSectorSize(uint32_t Sector);
uint32_t GetSector(uint32_t Address);
cs


1. MCU에서 사용할 메모리를 선택하기 전에 예상되는 코드 메모리를 계산.
현재 프로젝트를 진행하고 있는 프로그램에 적용할 생각으로 테스트를 하고 있는데, 프로그램 영역과 겹치지 않도록
남는 영역에 EEPROM Emulation 용 메모리를 선택해야 하므로, 프로그램 코드의 크기를 계산해 봐야 겠습니다.


[프로젝트로 진행중인 프로그램 크기 계산]




Total ROM Size = 37580(Code) + 1948(RO) + 344(RW) = 39872(0x9BC0)
Total RAM Size = 344(RW) + 71448(ZI) = 71792(0x11870)

64KByte = 64*1024 = 65536Byte(0x10000) 이므로, APP 프로그램(0x9BC0)는 0,1,2 Sector 범위에 들어 가고 약간 여분으로 프로그램을 더 추가 하더라도 3번 Sector를 넘어가진 않을 것 같습니다.

2. 위의 프로그램의 코드 계산의 토대로 메모리를 할당해 봤습니다.




APP 프로그램은 어드레스 0x0800 0000 부터 늘어나기 때문에, EEPROM Emulation 용 Flash 메모리는 뒤쪽에 안 쓰는 곳에 지정을 하는 게 맞고, 겨우 몇십 바이트 정도만 사용할 예정이라 가능한 작은 크기의 Sector를 할당하는 것이 좋겠습니다.

하지만, 지금 현재 개발 중인 APP 소스 크기가 39KB 에 달해서 Sector 0,1,2 를 사용하고 있습니다. 따라서 완료 시점까지 좀 여유있게 프로그램 메모리를 더 할당해야 해서 Sector 0~3 까지 64KB 를 할당하고, 아깝지만 EEPROM 영역을 Sector4(64KB) 로 할당을 해야 겠습니다. 뒤로 갈수록 섹터 크기가 커지니, 메모리가 어마어마하게 낭비되고 지우는 시간도 많이 걸릴 테지만, 128KB 를 쓰는 것은 더 아깝고 그다지 더 쓸 일도 없을 것 같으니 그만 안타까운 마음을 접기로 했습니다.

3. 버퍼 메모리(Read/Write 변수) 할당
EEPROM 이 아니라 지정된 메모리에 프로그램을 쓰려면, 전체 Sector를 지우고 변경할 내용을 한번에 써야하기에
메모리에 쓸 영역만큼의 SRAM 크기를 지정해야 합니다.
저는 아무리 사용해 봐야 512 개 정도면 충분할 것 같아서 512 바이트만큼 배열 변수로 할당했습니다.


4. EEPROM Emulator 테스트 프로그램 구상
- 메모리를 초기화 하기 위한 Flash 메모리 영역에 인식 ID 를 지정해서, 처음으로 메모리를 사용할 경우 디폴트 값으로 초기화를 하는 용도로 사용.
- 10-Byte의  Flash Memory 에, Nucleo 보드의 User-SW 를 1번 누를 때 마다 각각의 Byte 의 값이 1씩 증가되면서,
Write 한다. (현재 변화된 10-Byte의 Flash Memory 값을 읽어서 UART에 표시)
- 전원 Reset 시 10-Byte의 Flash Memory 값을 읽어서 UART에 표시하여 Flash 에 값이 남아있는지 확인. 


5. APP 프로그램의 코드영역이 EEPROM FLASH 영역을 침범하지 않도록 ROM Size 변경
ROM 크기를 기본 256KB 에서 64KB 로 변경해서, 만일 코드 사이즈가 커져서
EEPROM 영역에 데이터가 써지는 것을 방지한다.





6-0. eeprom emulation Test 프로그램의 #define .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 512KB
#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000/* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000/* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000/* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000/* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000/* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000/* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000/* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000/* Base @ of Sector 7, 128 Kbytes */
 
#define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_4   /* Start @ of user Flash area */
#define FLASH_USER_END_ADDR     FLASH_USER_START_ADDR  +  GetSectorSize(FLASH_SECTOR_4) -1 /* End @ of user Flash area : sector start address + sector size -1 */
 
#define USR_MEM_CNT            512
#define USR_ASIGN_ID        0xA5
#define USR_ASIGN_Add        0
#define USR_DATA0_Add        1
 
cs

6-1. eeprom emulation Test 프로그램의 main() 함수 프로그램 소스코드.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
int main(void)
{
 
  /* USER CODE BEGIN 1 */
    uint8_t flash_buffer[512];
    __IO uint32_t data32 = 0;
    
  /* USER CODE END 1 */
 
  /* MCU Configuration----------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
 
  /* USER CODE BEGIN 2 */
    printf("UART Test..!!\n\r");
    
    // Flash data memory Test..
    init_FLASH(flash_buffer);
    
    MEM_If_Read_FS ((uint8_t *)FLASH_USER_START_ADDR, flash_buffer, 512);
    dump_data(flash_buffer,0,16);
    
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    uint8_t sw_stat,old_sw_stat;
    old_sw_stat = sw_stat = HAL_GPIO_ReadPin(USER_SW_GPIO_Port,USER_SW_Pin);
    
  while (1)
  {
    old_sw_stat = sw_stat;
    sw_stat = HAL_GPIO_ReadPin(USER_SW_GPIO_Port,USER_SW_Pin);
        
    if (old_sw_stat != sw_stat)
    {
        if (sw_stat == 0)
        {
            proc_sw_push(flash_buffer);
            MEM_If_Read_FS ((uint8_t *)FLASH_USER_START_ADDR, flash_buffer, 512);
            dump_data(flash_buffer,0,16);
        }
    }
        HAL_Delay(50);
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
 
  }
  /* USER CODE END 3 */
 
}
cs


6-2. eeprom emulation Test 프로그램의 메모리 초기화 함수 프로그램 소스코드.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void init_FLASH(uint8_t *mem_buf)
{
    memset(mem_buf,0,USR_MEM_CNT);
    MEM_If_Read_FS ((uint8_t *)FLASH_USER_START_ADDR, mem_buf, USR_MEM_CNT);
    
    if (mem_buf[USR_ASIGN_Add] != USR_ASIGN_ID)
    {
        memset(mem_buf,0,USR_MEM_CNT);
        mem_buf[USR_ASIGN_Add] = USR_ASIGN_ID;
        mem_buf[USR_DATA0_Add+0= 0;
        mem_buf[USR_DATA0_Add+1= 1;
        mem_buf[USR_DATA0_Add+2= 2;
        mem_buf[USR_DATA0_Add+3= 3;
        mem_buf[USR_DATA0_Add+4= 4;
        mem_buf[USR_DATA0_Add+5= 5;
        mem_buf[USR_DATA0_Add+6= 6;
        mem_buf[USR_DATA0_Add+7= 7;
        mem_buf[USR_DATA0_Add+8= 8;
        mem_buf[USR_DATA0_Add+9= 9;
        
        MEM_If_Erase_FS(FLASH_USER_START_ADDR);
        MEM_If_Write_FS(mem_buf, (uint8_t *)FLASH_USER_START_ADDR,USR_MEM_CNT);
    }
}
cs

6-3. eeprom emulation Test 프로그램의 dump 함수 프로그램 소스코드.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
uint8_t dump_data(uint8_t *mem_buf,uint32_t start_mem,uint32_t cnt_mem)
{
    uint32_t i,cnt_i;
    
    if (start_mem < USR_MEM_CNT)
    {
        if ((start_mem+cnt_mem) > USR_MEM_CNT)
        {
            return 1;
        }
        else
        {
            for (cnt_i=0;cnt_i < cnt_mem;)
            {
                printf("Addr [%05X ] ",start_mem+cnt_i);
                if ((cnt_mem - cnt_i) < 16)
                {
                    uint16_t t_i;
                    t_i = (cnt_mem - cnt_i);
                    
                    for (i=0;i<t_i;i++,cnt_i++)
                    {
                        printf("%02X ",mem_buf[start_mem+cnt_i]);
                    }
                    printf("\n\r");
                }
                else
                {
                    for (i=0;i<16;i++,cnt_i++)
                    {
                        printf("%02X ",mem_buf[start_mem+cnt_i]);
                    }
                    printf("\n\r");
                }
            }
            return 0;
        }
    }
    else
    {
        return 2;
    }
}
cs

6-4. eeprom emulation Test 프로그램의 스위치처리 함수 프로그램 소스코드.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void proc_sw_push(uint8_t *mem_buf)
{
    static uint8_t cnt_push=0;
    
    cnt_push++;
    printf("cnt Push switch = [%3d]\n\r",cnt_push);
    mem_buf[USR_DATA0_Add+0]++;
    mem_buf[USR_DATA0_Add+1]++;
    mem_buf[USR_DATA0_Add+2]++;
    mem_buf[USR_DATA0_Add+3]++;
    mem_buf[USR_DATA0_Add+4]++;
    mem_buf[USR_DATA0_Add+5]++;
    mem_buf[USR_DATA0_Add+6]++;
    mem_buf[USR_DATA0_Add+7]++;
    mem_buf[USR_DATA0_Add+8]++;
    mem_buf[USR_DATA0_Add+9]++;
    
    MEM_If_Erase_FS(FLASH_USER_START_ADDR);
    MEM_If_Write_FS(mem_buf, (uint8_t *)FLASH_USER_START_ADDR,USR_MEM_CNT);
}
cs

7. 최종 UART 터미날로 본 결과 테스트




버튼을 누를 때 마다, 10 바이트의 값이 1씩 증가하고.. Power On-Reset 을 했더니 메모리 값이 변하지 않고,
남아 있는 것을 확인 할 수 있었습니다.


+ Recent posts