__DSI_UNMASK_TE()은 DSI에서 TE를 발생할 수 있도록 하는 명령어이다. TE 즉, Tearing Effect란 Display에서 하나의 화면 갱신이 끝난 이후, 다시 갱신할 수 있도록 주는 신호를 의미한다. 이후 발생하는 Callback은 아래와 같다.

void HAL_DSI_TearingEffectCallback(DSI_HandleTypeDef *hdsi)
{
  /* Mask the TE */
  __DSI_MASK_TE();
 
  /* Refresh the right part of the display */
  HAL_DSI_Refresh(hdsi);
}

다시 TE를 Mask하여 신호를 막은 후, HAL_DSI_Refresh()를 호출한다.


HAL_StatusTypeDef HAL_DSI_Refresh(DSI_HandleTypeDef *hdsi)
{
  /* Process locked */
  __HAL_LOCK(hdsi);
 
  /* Update the display */
  hdsi->Instance->WCR |= DSI_WCR_LTDCEN;
 
  /* Process unlocked */
  __HAL_UNLOCK(hdsi);
 
  return HAL_OK;
}

hdsi->Instance->WCR |= DSI_WCR_LTDCEN;해당 코드를 해설하면, “‘Write Control Resister’에를 조정하여 ‘LTDC’에서 준비된 데이터를 ‘DSI’를 통해 디스플레이로 전송하도록 허용한다.”고 해석 할 수 있다.

즉, DSI에서 LTDC 데이터를 받을 수 있도록 허용한다.

여기서 WCRDSI_WCR_LTDCEN를 설정한 직후에, 비트가 제거된다. 내부적으로 ‘LTDC’에서 ‘DSI’로 빠르게 전송을 완료한 이후에 비트를 제거하는듯 보인다.

(GPT 말로는 소프트웨어를 제어하여 수동으로 동작시켜야한다고 하는데, 추적이 어렵다…)


글을 더 작성하기에 앞서서 TE와 Refresh 신호의 차이에 대해서 정리해보자. 언듯 보기에 두 신호는 같은 역할을 가지는 것 같지만, 신호를 발생시키는 주체와 역할이 조금 다르다.

주요 차이와 관계

항목TE(Tearing Effect)Refresh
발생 주체LCD 패널DSI 컨트롤러
발생 시점LCD가 현재 프레임 표시를 완료했을 때TE 신호를 받은 뒤 DSI가 새 데이터를 LCD로 전송 완료했을 때
현재 BSP코드에서는 TE→Refresh→Refresh 순서로 동작하며 순환적으로 동작하게 된다.

‘DSI Host’는 가지고 있는 데이터가 생기면 즉시 LCD로 데이터를 전달한다. 따라서 DSI_WCR_LTDCEN를 설정한 직후, 전송이 이루어진다고 할 수 있다.

따라서 TE가 발생하면 원래 설정된 화면의 절반을 전송하여 갱신하고, 전송완료 이후, Refresh를 실행하여 나머지 절반을 갱신하고, 원래 설정으로 돌린 후에, 카메라를 재시동하는 방식으로 동작한다.


Refresh가 완료되었을 때, 발생하는 Callback을 살펴보자. (BSP 코드)

void HAL_DSI_EndOfRefreshCallback(DSI_HandleTypeDef *hdsi)
{
  if(pending_buffer >= 0)
  {
    if(active_area == LEFT_AREA)
    {
      /* Disable DSI Wrapper */
      __HAL_DSI_WRAPPER_DISABLE(hdsi);
      /* Update LTDC configuration */
      LTDC_LAYER(&hlcd_ltdc, 0)->CFBAR = LCD_FRAME_BUFFER + 400 * RGB565_BYTE_PER_PIXEL;
      __HAL_LTDC_RELOAD_CONFIG(&hlcd_ltdc);
      /* Enable DSI Wrapper */
      __HAL_DSI_WRAPPER_ENABLE(hdsi);
#if (USE_LCD_CTRL_OTM8009A > 0)
      HAL_DSI_LongWrite(hdsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, OTM8009A_CMD_CASET, pColRight);
#endif /* USE_LCD_CTRL_OTM8009A */
      /* Refresh the right part of the display */
      HAL_DSI_Refresh(hdsi);
 
    }
    else if(active_area == RIGHT_AREA)
    {
      /* Disable DSI Wrapper */
      __HAL_DSI_WRAPPER_DISABLE(&hlcd_dsi);
      /* Update LTDC configuration */
      LTDC_LAYER(&hlcd_ltdc, 0)->CFBAR = LCD_FRAME_BUFFER;
      __HAL_LTDC_RELOAD_CONFIG(&hlcd_ltdc);
      /* Enable DSI Wrapper */
      __HAL_DSI_WRAPPER_ENABLE(&hlcd_dsi);
#if (USE_LCD_CTRL_OTM8009A > 0)
      HAL_DSI_LongWrite(hdsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, OTM8009A_CMD_CASET, pColLeft);
#endif /* USE_LCD_CTRL_OTM8009A */
 
      if(cameraState == CAMERA_STATE_DISPLAY_ONGOING)
      {
        cameraState = CAMERA_STATE_CAPTURE_ONGOING;
        BSP_CAMERA_Resume(0);
      }
 
      pending_buffer = -1;
    }
  }
  active_area = (active_area == LEFT_AREA)? RIGHT_AREA : LEFT_AREA;
}

이전에 했던 설명대로 반대편을 갱신하고, 원래대로 돌린 후에 카메라를 재시동한다.