제가 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


+ Recent posts