STM32 MCU는 칩마다 각각 다른 96bit(12-Byte)의 고유번호(Unique ID)를 갖고 있다.


이 고유 번호는 종종 쓸모가 있다. 
- 1회용 싸구려 소모품이 아닌 이상, 제품 마다 이력 관리를 할 수도 있고,
- 가공 해서 암호를 만들 수도 있으며,
- 무선 장비의 경우에는 맥어드레스라고 하여, 서로 간의 통신할 때 충돌을 방지하는 용도로도 사용할 수 있다.


고유 번호가 없을 경우, 칩마다 각각 플래쉬 메모리에 서로 다른 번호를 만들어서 넣어 줘야 하는데,
여간 불편한 게 아니다.

고유 아이디를 만드려면, 코드에 직접 넣어서 컴파일해서 MCU에 프로그램해서 넣는 방법과 
통신을 통해서 프로토콜로 플래쉬의 특정 주소에 써 넣는 방법이 주로 사용될 것이다.

고유 아이디를 만들어서 플래쉬에 쓰는 경우에는, 
플래쉬에 썼기 때문에 잘못해서 지워지는 경우가 있을 수 있고, 
사람이 하다 보니 중복해서 쓰거나 안 쓰고 넘어가는 경우도 종종 있다.

또한 칩 하나하나 마다 컴파일을 하거나 통신 프로토콜로 아이디를 쓰려면 툴을 사용해야 하고 시간도 많이 걸리니 모든것이 낭비여서, 고유 아이디가 제공된다는 점은 어떻게 보면 큰 장점이다.

STM32F4 MCU 에서 제공된 고유 아이디를 읽으려면, 고유 아이디가 위치한 특정 어드레스에서 값을 읽으면 된다.

아주 간단한 예로 끝내겠다. 
다음 예는 유니크 아이디가 위치한 주소(0x1FFF7A10)에서 12바이트를 
8비트/16비트/32비트 단위로 읽어서 각각 UART로 출력하는 예이다.
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
    #define ID_UNIQUE_ADDRESS        0x1FFF7A10
 
    uint8_t i;
    uint8_t uu_id_8b[12];
    uint16_t uu_id_16b[6];
    uint32_t uu_id_32b[3];
    
    // 8-bit 12-EA Read
    for (i=0;i<12;i++)
        uu_id_8b[i] = *(uint8_t *)(ID_UNIQUE_ADDRESS+i);
    // 16-bit 6-EA Read
    for (i=0;i<6;i++)
        uu_id_16b[i] = *(uint16_t *)(ID_UNIQUE_ADDRESS+i*2);
    // 32-bit 3-EA Read
    for (i=0;i<3;i++)
        uu_id_32b[i] = *(uint32_t *)(ID_UNIQUE_ADDRESS+i*4);
 
    printf("UUID[8-Bit]  ");
    for(i=0;i<12;i++)
        printf(":%02X",uu_id_8b[i]);
 
    printf("\n\rUUID[16-Bit] ");
    for(i=0;i<6;i++)
        printf(":%04X",uu_id_16b[i]);
 
    printf("\n\rUUID[32-Bit] ");
    for(i=0;i<3;i++)
        printf(":%08X",uu_id_32b[i]);
 
    while(1);
cs





참고로 다음과 같은 정보도 있다.
읽는 방식은 메뉴얼을 참고로 위의 코드 예를 조금 변경하면 되겠고, 
더 자세한 정보는 링크에 가서 코드를 다운 받아서 보면 됩니다.
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
/**
 * @brief  Get STM32F4xx device signature
 * @note   Defined as macro to get maximal response time
 * @param  None
 * @retval Device signature, bits 11:0 are valid, 15:12 are always 0.
 *           - 0x0413: STM32F405xx/07xx and STM32F415xx/17xx)
 *           - 0x0419: STM32F42xxx and STM32F43xxx
 *           - 0x0423: STM32F401xB/C
 *           - 0x0433: STM32F401xD/E
 *           - 0x0431: STM32F411xC/E
 */
/**
 * @brief  Get STM32F4xx device revision
 * @note   Defined as macro to get maximal response time
 * @param  None
 * @retval Device revision value
 *           - 0x1000: Revision A
 *           - 0x1001: Revision Z
 *           - 0x1003: Revision Y
 *           - 0x1007: Revision 1
 *           - 0x2001: Revision 3
 */
#define ID_DBGMCU_IDCODE        0xE0042000
 
 
/**
 * @brief  Get STM32F4xx device's flash size in kilo bytes
 * @note   Defined as macro to get maximal response time
 * @param  None
 * @retval Flash size in kilo bytes
 */
#define ID_FLASH_ADDRESS        0x1FFF7A22
cs




------------------------------------------------------------------------------------------------





------------------------------------------------------------------------------------------------




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

외부에 추가로 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 을 했더니 메모리 값이 변하지 않고,
남아 있는 것을 확인 할 수 있었습니다.


제가 stm32f411로 테트트하다 보니, 가끔 인터럽트를 사용하면 이상동작을 하는 경우가 있어서,

이전에 만든 DWT Delay 함수를 사용하지 못해서 Polling 으로 micro second Delay 함수를 또 찾아봤습니다.

정말 간단하게 만들 수 있었습니다. 아주 정확하지는 않겠지만, 
system 클럭의 정보를 바탕으로 1clk 당 실행시간을 계산해서 us Delay를 만들어 내는 것으로 보입니다.
따라서 system clk 이 바뀌더라도 대충 맞는 그런그런 정도(아주 정밀하지는 않음)의 딜레이 함수입니다.

제가 구글링에서 참고한 사이트는 다음과 같습니다.

그리고 제가 사용자 함수를 모아 놓는 파일인 user_def.c 와 user_def.h 가 있는데,
user_def.h 파일에만 다음과 같은 함수를 정의해 놓습니다.
1
2
3
4
5
6
7
8
__STATIC_INLINE void Pol_Delay_us(volatile uint32_t microseconds)
{
  /* Go to number of cycles for system */
  microseconds *= (SystemCoreClock / 1000000);
 
  /* Delay till end */
  while (microseconds--);
}
cs


그리고 이 user_def.h 를 인클루드한 소스파일에서 다음과 같이 사용하면 됩니다.
1
2
3
    Pol_Delay_us(50);        // Polling 50us
    //DWT_Delay_us(100);    // DWT 100us
    //HAL_Delay(1);            // HAL Library 1ms
cs


라이브러리로 제공되는 함수가 milisec 함수(HAL_Delay()) 는 있는데,

가끔 가다가 필요한 us 함수를 구글링해서 찾았고, 잘 동작합니다.

링크는 다음과 같습니다. 
https://community.st.com/thread/13838 에서 Knut Knusper 가 쓴 글을 참조하시면 됩니다.

cubemx 로 여러가지 테스트해보다 보면 실수로 User code 부분이 싹~ 날아가는 경우가 많아서,
이곳에 코드를 다시 정리해 봅니다.

저는 user_def.c 라는 파일을 하나 만들어서 cubemx 에서 갑자기 지워지는 것을 막기 위해 필요한 함수를 정의해 놓아서,
새로 추가된 DWT_Delay_Init() 함수는 이곳에 정의해 놓았습니다.

1. user_def.c 에 다음의 코드를 추가
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
uint32_t DWT_Delay_Init(void) {
  /* Disable TRC */
  CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk; // ~0x01000000;
  /* Enable TRC */
  CoreDebug->DEMCR |=  CoreDebug_DEMCR_TRCENA_Msk; // 0x01000000;
     
  /* Disable clock cycle counter */
  DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; //~0x00000001;
  /* Enable  clock cycle counter */
  DWT->CTRL |=  DWT_CTRL_CYCCNTENA_Msk; //0x00000001;
     
  /* Reset the clock cycle counter value */
  DWT->CYCCNT = 0;
     
     /* 3 NO OPERATION instructions */
     __ASM volatile ("NOP");
     __ASM volatile ("NOP");
  __ASM volatile ("NOP");
 
  /* Check if clock cycle counter has started */
     if(DWT->CYCCNT)
     {
       return 0/*clock cycle counter started*/
     }
     else
  {
    return 1/*clock cycle counter not started*/
  }
}
cs

2. user_def.h 에 다음의 코드 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
uint32_t DWT_Delay_Init(void);
 
 
 
/**
 * @brief  This function provides a delay (in microseconds)
 * @param  microseconds: delay in microseconds
 */
__STATIC_INLINE void DWT_Delay_us(volatile uint32_t microseconds)
{
  uint32_t clk_cycle_start = DWT->CYCCNT;
 
  /* Go to number of cycles for system */
  microseconds *= (HAL_RCC_GetHCLKFreq() / 1000000);
 
  /* Delay till end */
  while ((DWT->CYCCNT - clk_cycle_start) < microseconds);
}
cs


3. main.c 의 main() 함수에서 us Delay 함수(DWT_Delay_us()) 사용.
1
2
3
4
5
6
    while(1)
    {
        HAL_GPIO_TogglePin(IND_LED_FL_GPIO_Port,IND_LED_FL_Pin);
        DWT_Delay_us(1000000);    // 1sec
    }
 
cs


1000,000 us (1 sec) 마다 GPIO 가 토글되는 것을 확인했으니, 동작이 잘 되네요. ^^


STM32F411 에서 타이머2 인터럽트를 사용하다가, 

동작이 생각처럼 안되어서 쌩쑈를 하다가 나중에 동작이 되긴 했지만 아직까지 이해가 안되는 부분이 있었습니다.

인터럽트를 처리하는 콜백 함수와 함수 내에서 사용하는 전역 변수들은 다음과 같고, main.c 파일 안에 있습니다.
이 콜백함수는 1ms 마다 호출되도록 설정했고, 
콜백 함수 내에 f_tim2_cpl 가 SET 되는 부분에 LED 와 연결된 포트를 토글하면 잘 동작 됐습니다.

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
uint8_t f_tim2_cpl=0;
uint16_t cnt_tim2[5= {0,0,0,0,0};
uint16_t ref_tim2[5= {50,2000,40,0,0};
 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    uint8_t cnt_tmr;
    
    if (htim->Instance == TIM2)
    {
        for(cnt_tmr=0;cnt_tmr<5;cnt_tmr++)
        {
            if (cnt_tim2[cnt_tmr] < ref_tim2[cnt_tmr])
            {
                cnt_tim2[cnt_tmr]++;
            }
            if (cnt_tim2[cnt_tmr] == ref_tim2[cnt_tmr])
            {
                cnt_tim2[cnt_tmr] = 0;
                switch (cnt_tmr)
                {
                    case 0:
                        f_tim2_cpl |= F_TIMCMPL_0;
                        break;
                    case 1:
                        f_tim2_cpl |= F_TIMCMPL_1;
                        break;
                    case 2:
                        f_tim2_cpl |= F_TIMCMPL_2;
                        break;
                    case 3:
                        f_tim2_cpl |= F_TIMCMPL_3;
                        break;
                    case 4:
                        f_tim2_cpl |= F_TIMCMPL_4;
                        break;
                }
            }
        }
    }
    else if (htim->Instance == TIM1)
    {
    }
}
cs

그런데, main 함수 내에서 다음과 같이 f_tim2_cpl 의 상태를 while 문의 조건문 안에서 읽어서 비교하면 동작이 안되고,
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
int main(void)
{
  uint16_t i;
  /* 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_USART1_UART_Init();
  MX_TIM2_Init();
 
  /* USER CODE BEGIN 2 */
  for(i=0;i<5;i++)
    cnt_tim2[i] = 0;
  f_tim2_cpl=0;
  HAL_TIM_Base_Start_IT(&htim2);
 
  printf("uart test\n\r");
  while((f_tim2_cpl&F_TIMCMPL_1) == 0)    // 2sec
  {
  }
  printf("2 sec delay\n\r");
 
  while(1);
}
cs


while() 문 안에, 여러가지 내용을 넣어 봤는데 동작이 되는 경우도 있고 안되는 경우도 있고,
안에 넣은 코드에 따라서 각각 다릅니다.
1. HAL_delay(1); 을 넣은 경우. (정상 동작)
1
2
3
4
5
6
7
    printf("uart test\n\r");
    while((f_tim2_cpl&F_TIMCMPL_1) == 0)    // 2sec
    {
        HAL_Delay(1);
    }
    printf("2 sec delay\n\r");
 
cs
2. __wfi(); //wait for interrupt 을 넣은 경우. (정상 동작)
1
2
3
4
5
6
7
    printf("uart test\n\r");
    while((f_tim2_cpl&F_TIMCMPL_1) == 0)    // 2sec
    {
        __wfi();
    }
    printf("2 sec delay\n\r");
 
cs
3. __nop(); // no opration 을 넣은 경우. (동작 안함)
1
2
3
4
5
6
7
    printf("uart test\n\r");
    while((f_tim2_cpl&F_TIMCMPL_1) == 0)    // 2sec
    {
        __nop();
    }
    printf("2 sec delay\n\r");
 
cs
4. printf() 문 을 넣은 경우, (정상 동작)
1
2
3
4
5
6
7
    printf("uart test\n\r");
    while((f_tim2_cpl&F_TIMCMPL_1) == 0)    // 2sec
    {
        printf("timer 2 Whay?\n\r");
    }
    printf("2 sec delay\n\r");
 
cs
5. i++; (변수 1씩 증가). (동작 안함).
1
2
3
4
5
6
7
    printf("uart test\n\r");
    while((f_tim2_cpl&F_TIMCMPL_1) == 0)    // 2sec
    {
        i++;
    }
    printf("2 sec delay\n\r");
 
cs
6. for() 루프로 딜레이. (동작 안함)
1
2
3
4
5
6
7
8
9
10
    printf("uart test\n\r");
    while((f_tim2_cpl&F_TIMCMPL_1) == 0)    // 2sec
    {
        for(i=0;i<1000;i++)
        {
            __nop();
        }
    }
    printf("2 sec delay\n\r");
 
cs

지금까지의 테스트로 봤을 때, 
인터럽트에 의한 전역 변수의 상태를 읽으려면
- 단순한 for 루프나 폴링 방식의 딜레이로는 안되고,
- __wfi(), HAL_delay(), printf() 의 함수가 사용되어야 읽을 수 있다고 볼 수 있겠습니다.

흠, 지금까지 이런 것을 염두에 두고 인터럽트를 쓰지 않았는데.. 거의 무리없이 지나왔었습니다.
그런데 이제와서 이런 경우를 겪어서 좀 황당하네요.

ㅜㅜ 뭔가 기초가 부족한 것 같습니다. 혹시 누가 정확한 원인을 아시는 분 설명 좀 부탁 드려요.

최종적으로 다음과 같이 사용하니, 별 문제가 없네요. (__wfi() 사용)
1
2
3
4
5
6
7
8
9
10
while(1)
{
    __wfi();
    if (f_tim2_cpl & F_TIMCMPL_2)         // 400ms T compare
    {
        cnt_tim2[2= 0;                        // 400ms counter
        f_tim2_cpl &= ~F_TIMCMPL_2;  // 400ms Flag clear
        HAL_GPIO_TogglePin(IND_LED_FL_GPIO_Port,IND_LED_FL_Pin);    // LED toggle
    }
}
cs


4.2 로 바뀌면서 안되는 것이 정말 저에게는 치명적이라,


USB MSC 가 안되서 4.2 에서 돌아가게 해 보려는데 도무지 방법이 없네요.

누가 4.19 버전 갖고 계시면 좀 보내 주시면 감사하겠습니다.

cubemx 4.16 버전 받았습니다. 필요하신 분은, 댓글에 링크 걸어 두었습니다.

그래도 혹시 4.19 버전 갖고 있으신분, 링크 부탁드립니다.

하하, CUBEMX 4.19 버전도 구했습니다. ST 대리점에 부탁해서 얻었습니다. 휴~~

다시 링크 해 드립니다. 혹시 저와 같은 경우로 어려움을 겪으시는 분들이 계시면 다운 받으시기 바랍니다.
1. cubemx v4.16 : 링크
2. cubemx v4.19 : 링크


USB MSC 도 안되고, 


HAL_SD_CardInfoTypedef 도 바뀌었습니다.

BSP_SD_ReadBlocks_DMA(),BSP_SD_WriteBlocks_DMA() 함수도 바뀌었고,

MX_SDMMC1_SD_Init() 함수도 이상하다고 하네요.

얼마 전에, 클럭 조차도 동작 안하던 버그가 있었고요.

아무래도, CUBEMX 4.2 는 쓰지 말고, 이전 버전으로 돌아가야할 듯 합니다.


2018 년 3월 18일에 일어난 업데이트 버그입니다. 예전 글을 1개씩 옮기는 중이라, 현재 일어난 일 인줄 착각 할까봐 이 글을 추가합니다.


CUBEMX 4.2 업데이트를 하고 나서,

KEIL 컴파일러 코드 생성 시, 심각한 버그가 발견 되었습니다.

HCLK 의 초기화가 잘못 되어서 프로그램이 동작되지 않습니다.

SystemClock_Config() 함수 내용 중, 1줄이 잘못 되었는데, 예전 코드로 수정해야 동작이 되네요.

SystemClock_Config() 예전 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void SystemClock_Config(void)
{
 
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
cs

SystemClock_Config() CUBEMX 4.20 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void SystemClock_Config(void)
{
 
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE;
cs


SystemClock_Config() CUBEMX 4.20 코드(수정)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void SystemClock_Config(void)
{
 
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  //RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
cs


처음으로 SD-Card 를 납땜을 해서, 동작이 잘 되는지 확인을 해 봤습니다.


테스트 방법은, uSD-Card 를 마운트 하고 디렉토리 스캔을 해서 USB_CDC 로 터미널에 뿌려서
안에 넣어둔 파일이 제대로 보이는지 확인하면 되겠습니다.

다음은 보드에 uSD-Card 를 납땜한 모습입니다.




uSD-Card 회로도는 다음과 같습니다. STM32F4xx 에 SDIO-4bit 방식으로 연결했습니다.




다음은 ST 사의 CUBEMX 툴로 SDIO-4BIT 를 추가한 핀맵입니다.




다음은 디렉토리 스캔 함수 인데, 출처는 기억이 안납니다. main.c 파일에 붙여서 사용하면 됩니다.
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
void f_opendir_scan(void)
{
    #if _USE_LFN
    TCHAR lfn[_MAX_LFN + 1];
    fno.lfname = lfn;
    fno.lfsize = sizeof lfn;
    #endif
    TCHAR path[200= "";
    
    res = f_mount(&fs32,SD_Path,1);
    printf("SD Mount : res f_mount : %02X\n\r",res);
    
    if (res == FR_OK)
    {
    res = f_opendir(&dir,path);
        printf("res f_open : %02X\n\r",res);
        
        if (res == FR_OK)
        {
        while(1)
        {
            char *fn;
             
            res = f_readdir(&dir, &fno);
             
            if (res != FR_OK)
                printf("res = %d f_readdir\n\r", res);
 
            if ((res != FR_OK) || (fno.fname[0== 0))
                break;
 
#if _USE_LFN
      fn = *fno.lfname ? fno.lfname : fno.fname;
#else
      fn = fno.fname;
#endif
            printf("%c%c%c%c ",
                ((fno.fattrib & AM_DIR) ? 'D' : '-'),
                ((fno.fattrib & AM_RDO) ? 'R' : '-'),
                ((fno.fattrib & AM_SYS) ? 'S' : '-'),
                ((fno.fattrib & AM_HID) ? 'H' : '-') );
 
            printf("%10d ", fno.fsize);
             
            printf("%s/%s\n\r", path, fn);
        }        
        }
 
        res = f_mount(0,SD_Path,0);
        printf("SD Unmount : res f_mount : %02X\n\r",res);
    }
}
 
cs

다음은 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
/* Private variables ---------------------------------------------------------*/
HAL_SD_ErrorTypedef res_sd;
 
FRESULT res;
FILINFO fno;
FIL fil;
DIR dir;
FATFS fs32;
 
uint32_t byteswritten, bytesread; /* File write/read counts */
 
 
int main(void)
{
  /* 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_DMA_Init();
  MX_TIM2_Init();
  MX_USB_DEVICE_Init();
  MX_SDIO_SD_Init();
  MX_FATFS_Init();
 
  /* USER CODE BEGIN 2 */
  // HAL_GPIO_WritePin(SDPWR_GPIO_Port, SDPWR_Pin, GPIO_PIN_RESET);
  HAL_Delay(500);
  HAL_TIM_Base_Start_IT(&htim2);
    
  f_opendir_scan();
 
  while (1)
  {
 
  }
}
cs

그리고 나서 프로그램을 다운로드 하고 실행을 하니, uSD-Card 의 내용이 보입니다.
납땜이 제대로 잘 됐군요.^^




얼마 전에 CUBEMX 업데이트를 했는데, 못보던 LIB가 1개 보이네요.





아직 쓸 일이 없고, 어떻게 쓰는 줄도 모르겠지만, JPEG encode,decode 함수가 있네요.
jpeg_CreateCompress() , jpeg_CreateDecompress()

나중에 쓸 일 있으면 써봐야겠다. ^^.

아뭏든 나온지 보름 정도 밖에 안된 따끈따끈한 라이브러리 랍니다. (2017,3,2)


+ Recent posts