본문으로 바로가기

적외선 수신기 기능 구현

category MCU/STM32F429 2020. 12. 3. 01:20
반응형

적외선 수신기는 IR리시버(수신기)라고도 하는데 여기서 IR은

Infrared의 약자로 적외선을 뜻합니다. 적외선 특성을 이용하기 

때문에 사람이 볼 수 있는 가시광선 외 영역이라 눈으로 볼 수 

없습니다.


적외선 통신은 UART나 I2C처럼 표준화된 통신이 아니라 제조사마다

프로토콜이 다르며 보통 NEC 프로토콜을 사용합니다. 

이번 수신기 구현에 사용될 리시버는 KSM-60으로 NEC 프로토콜을

사용합니다. 




 NEC 프로토콜


  • Start : 9ms Pulse 유지 후 4.5ms Space
  • Address : 8bit Data
  • Address Inverse : Address 보수 8bit Data
  • Command : 8bit Data
  • Command Inverse : Command 보수 8bit Data

데이터 전송 시 보수(Inverse)를 같이 보내주는 이유는 체크섬과 마찬가지로

정확하게 데이터가 전송이 되었는지 확인하기 위함입니다.




  • Logical '0' : 562.5us pulse 유지 후 562.5us Space
  • Logical '1' : 562.5us pulse 유지 후 1.6875ms Space




 적외선 수신 구현 방법


1)50us 타이머 인터럽트 구현.

- Falling Edge에서 카운트.


2)적외선 수신기의 신호를 받을 인터럽트 구현.

- Falling Edge

- Pull-up


3)Start Bit 이후 Falling Edge가 들어오면 Sampling 시작.


- 50us 마다 카운트 시 '1'인 경우 22 ~ 23

'0'인 경우 44 ~ 45로 Threshold를 27로 임의로 지정.

- Falling Edge에서 카운트가 Threshold 보다 작으면 '1'.

- Falling Edge에서 카운트가 Threshold 보다 크면 '0'.


4)Sampling이 완료되면 출력.




 MCU 설정 


    • 회로구성

    회로 구성은 간단합니다. 

    1번핀 신호를 받을 인터럽트가 필요합니다.


    • GPIO 설정

    Out과 연결되는 인터럽트를 설정합니다.

    Falling Edge, Pull-up을 설정해줍니다.


    • 타이머 설정

    50us의 타이머가 필요합니다.


    타이머 인터럽트도 Enable 해줍니다.





     IR 수신 예제 코드 


    • 인터럽트 핸들러
    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
    void EXTI2_IRQHandler(void)
    {
      /* USER CODE BEGIN EXTI2_IRQn 0 */
     
      /* USER CODE END EXTI2_IRQn 0 */
      HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
      /* USER CODE BEGIN EXTI2_IRQn 1 */
      IsFalling = ENABLE;
      /* USER CODE END EXTI2_IRQn 1 */
    }
     
    /**
      * @brief This function handles TIM2 global interrupt.
      */
    void TIM2_IRQHandler(void)
    {
      /* USER CODE BEGIN TIM2_IRQn 0 */
     
      /* USER CODE END TIM2_IRQn 0 */
      HAL_TIM_IRQHandler(&htim2);
      /* USER CODE BEGIN TIM2_IRQn 1 */
      IncreaseTick();
      /* USER CODE END TIM2_IRQn 1 */
    }
     


    Falling Edge에서  IsFalling를 ENABLE해주고 50us마다 gTick을 1증가 시킵니다.

    콜백함수에 안넣고 그냥 핸들러에 넣었습니다.


    • main.h
    1
    2
    3
    4
    5
    enum _SamplingStep{
      SAMPLING_READY = 0x00,
      SAMPLING_START,
      SAMPLING_DATA       
    };



    • 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
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    volatile int gTick = 0 ;
    volatile uint8_t IsFalling;
     
    void IncreaseTick(void)
    {
      gTick = gTick + 1;
    }
     
    void ResetTick(void)
    {
      gTick = 0;
    }
     
    int GetTick(void)
    {
      return gTick;
    }
     
    void TestIR(void)
    {
      uint32_t GetData = 0
      uint8_t bStep = 0;
      uint8_t Cnt = 0;
      int Tick = 0
     
      while(1){    
        switch(bStep){
          case SAMPLING_READY :
            if(IsFalling){            
              IsFalling = DISABLE;    
              ResetTick();
              bStep++;
            }          
            break;
          
          case SAMPLING_START:               
            if(IsFalling){            
              ResetTick();          
              IsFalling = DISABLE;                                             
              GetData = 0;          
              Cnt = 0;
              bStep++;
            }
            break;
            
          case SAMPLING_DATA:
            Tick = GetTick();
            
            if(IsFalling){
              if(Tick < THRESHOLD){ 
                GetData |= 0 << Cnt;
              }
              else if(Tick < (THRESHOLD * 2)){
                GetData |= 1 << Cnt;
              }
              else{
                bStep = SAMPLING_START;
                ResetTick();
                GetData = 0;
                Cnt = 0;
                break;
              }
              Cnt++;
              ResetTick();
              IsFalling = DISABLE;
            }
          
            if(Cnt == 32){             
              if(GetData == 0x827d0ff0){            
                HAL_GPIO_TogglePin(GPIOG, LD3_Pin | LD4_Pin);            
              }
              printf("%x\r\n", GetData);
              bStep = SAMPLING_START;
        
            }        
            break;             
        }
      }
    }





     코드 설명 


    HAL_TIM_Base_Start_IT(&htim2);

    먼저 gTick을 증가시키기 위해 

    TIM2의 인터럽트를 활성화시켜야 합니다.

    gTick은 IR신호로 부터 0과 1을 판단하기 위해

    사용됩니다.


    • InceraseTick
    1
    2
    3
    4
    void IncreaseTick(void)
    {
      gTick = gTick + 1;
    }

    gTick을 1씩 증가시킵니다. 

    타이머 인터럽트에서 50us에 1씩 증가.


    • ResetTick
    1
    2
    3
    4
    void ResetTick(void)
    {
      gTick = 0;
    }


    gTick을 0으로 초기화시킵니다.


    • GetTick
    1
    2
    3
    4
    int GetTick(void)
    {
      return gTick;
    }


    gTick의 값을 반환합니다.

    Falling Edge 후 다음 Falling Edge까지의 시간을

    얻기 위해 사용합니다.


    • Sampling Ready
    1
    2
    3
    4
    5
    6
    7
    case SAMPLING_READY :
            if(IsFalling){            
              IsFalling = DISABLE;    
              ResetTick();
              bStep++;
            }          
            break;


    Sampling Start Bit가 들어오면 Sampling시작

    준비를 합니다.


    • Sampling Start
    1
    2
    3
    4
    5
    6
    7
    8
    9
    case SAMPLING_START:               
            if(IsFalling){            
              ResetTick();          
              IsFalling = DISABLE;                                             
              GetData = 0;          
              Cnt = 0;
              bStep++;
            }
            break;


    Sampling을 시작합니다.


    • Sampling Data
    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
    case SAMPLING_DATA:
            Tick = GetTick();
            
            if(IsFalling){
              if(Tick < THRESHOLD){ 
                GetData |= 1 << Cnt;
              }
              else if(Tick < (THRESHOLD * 2)){
                GetData |= 0 << Cnt;
              }
              else{
                bStep = SAMPLING_START;
                ResetTick();
                GetData = 0;
                Cnt = 0;
                break;
              }
              Cnt++;
              ResetTick();
              IsFalling = DISABLE;
            }
          
            if(Cnt == 32){             
              if(GetData == 0x827d0ff0){            
                HAL_GPIO_TogglePin(GPIOG, LD3_Pin | LD4_Pin);            
              }
              printf("%x\r\n", GetData);
              bStep = SAMPLING_START;
        
            }        
            break;


    THRESHOLD 값은 27입니다. THRESHOLD 기준으로 '0'과 '1'이 구분되고

    32bit로 변환이 됩니다. 

    Sampling이 완료가 되면 데이터(GetData)를 출력합니다.

    특정값(0x827d0ff0)이면 LED를 Toggle시킵니다.


    다른 리모컨도 동일한지는 모르겠지만

    제가 갖고 있는 리모컨은 버튼이 누르는 동안 

    신호가 계속들어오는 형태였습니다.


    32bit는 정상적으로 들어오고 이후 데이터는 쓰레기

    값이 들어오기 때문에 else조건을 통해 32bit 이후 값으로

    인식하고 Step을 Ready로 돌아갑니다.





     결과 확인


    터미널 창에 값이 일정하게 들어오고 있습니다.

    827DFF0를 잘 보시면 프로토콜과 동일합니다.


    Address : 82

    Inverse Address : 70(Address 보수)

    Command : 0F

    Inverse Command : F0(Command보수)


    코드가 좀 조잡하게 만들었는데 잘 동작하네요.

    이상으로 마치겠습니다.



    2020/04/30 - [MCU/STM32F429] - STM32 타이머 제어하기(STM32F429I Discovery)

    2020/05/10 - [MCU/STM32F429] - STM32 Semihosting(STM32F429I Discovery)

    2020/04/05 - [MCU/STM32F429] - GPIO 입출력 제어하기(STM32F429I Discovery)

    2020/03/28 - [MCU/STM32F429] - CubeMX Setting 하기

    반응형