주요 내용으로 건너뛰기

MIPI-DSI 명령 모드

이 섹션에서는 명령 모드용 MIPI DSI 인터페이스를 구성하는 방법과 TouchGFX Generator에서 이 구성을 사용하는 방법에 대해 설명합니다. 이 문서에서 사용되는 예제는 16비트 RGB565 프레임 버퍼 형식에 대한 것으로, 일반적으로 STM32CubeMX와 코드 예제에서 다음과 같은 구성 요소들을 살펴봅니다

  • LTDC IP
  • DSI-HOST IP
  • TouchGFX Generator

STM32CubeMX - 하드웨어 구성

LTDC

  • 모드
    • Display TypeRGB565 (16 bits) - DSI Mode로 설정
  • 계층 설정
    • Number of layers1 layer로 설정
    • Windows Position, Frame Buffer Line LengthFrame Buffer Number of Lines에서 화면 해상도 설정
    • Layer 0 - Pixel FormatRGB565로 설정
    • Layer 0 - Alpha constant for blending255로 설정

      LTDC 구성

  • NVIC 설정
    • LTDC global interruptLTDC global error interrupt 모두 사용하지 않으므로 비활성화해야 합니다.

      LTDC NVIC 설정

DSIHOST 구성

  • 모드
    • DSIHost를 _*Adapted Command Mode with TE Pin**으로 설정
  • 디스플레이 인터페이스
    • Color CodingRGB565 (16 bits) - DSI mode로 설정
    • Maximum Command Size를 디스플레이 폭에 맞는 숫자로 설정
    • The Refresh of the Display Frame Buffer is Triggeredmanually by Enabling the LTDC로 설정
    • 나머지 구성은 선택한 LCD HW에 따라 다릅니다

      DSIHOST 구성

  • NVIC 설정
    • DSI global interrupt 활성화

      DSIHOST NVIC 설정

STM32CubeMX - TouchGFX Generator

  • 모드
    • Graphics Application 활성화
  • TouchGFX Generator
    • Display / InterfaceParallel RGB (LTDC)로 설정(여전히 애플리케이션이 통신해야 하는 컨트롤러이기 때문)
    • Application Tick SourceCustom으로 설정

      TouchGFX Generator 구성

DSIHOST / LTDC 초기화 시퀀스

  • MX_DSIHOST_DSI_Init()에 대한 호출은 MX_LTDC_Init()보다 먼저 수행되어야 합니다. 이 호출은 CubeMX에서 처리해야 합니다.

  • 사용자가 MX_LTDC_Init() 끝에 사용하는 LCD 컨트롤러에서 요구하는 초기화 코드를 추가해야 합니다. 이러한 코드는 대개 DSI HAL API인 HAL_DSI_ShortWrite()HAL_DSI_LongWrite()를 기반으로 합니다.

    /**
    * @brief LTDC Initialization Function
    * @param None
    * @retval None
    */
    static void MX_LTDC_Init(void)
    {
    ...

    /* 사용자 코드 시작 LTDC_Init 2 */

    __HAL_LTDC_DISABLE(&hltdc);
    HAL_DSI_Start(&hdsi);

    // 특정 LCD 컨트롤러의 초기화 코드
    ...

    // 첫 프레임까지 디스플레이 끄기
    HAL_DSI_ShortWrite(&hdsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, DSI_SET_DISPLAY_OFF, 0x00);

    __HAL_LTDC_ENABLE(&hltdc);

    /* 사용자 코드 끝 LTDC_Init 2 */

    ...
    }

DSI 명령 모드에서 TouchGFXHAL 클래스 업데이트

이 애플리케이션의 첫 번째 프레임을 렌더링할 때까지 MIPI DSI 디스플레이가 켜지지 않게 할 수 있는 한 가지 방법은 TouchGFX에서 첫 번째 프레임을 렌더링할 때까지 디스플레이를 꺼진 상태로 유지하도록 TouchGFXHAL::endFrame 함수를 보호하는 것입니다. TouchGFXHAL::endFrame()을 아래와 같이 업데이트해서 PWM 출력용으로 구성된 HW 타이머를 통해 LCD와 그 백라이트를 활성화할 수 있습니다.

void TouchGFXHAL::endFrame()
{
if (!display_on)
{
display_on = true;
/* LCD를 활성화하고 디스플레이 켜기 DCS 명령을 디스플레이에 보내기 */
HAL_DSI_ShortWrite(&hdsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, DSI_SET_DISPLAY_ON, 0x00);
/* PWM Timer 채널 시작 */
(void)HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
/* 밝기를 100%로 설정해 백라이트 활성화 */
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, 2U * 100);
}

TouchGFXGeneratedHAL::endFrame();
}

DSI 명령 모드에서 LTDC를 사용하여 window에 쓰도록 하여 디스플레이 내부 GRAM을 업데이트합니다. Window 크기를 전체 화면 영역으로 설정할 수 있습니다.

  static void MX_LTDC_Init(void)
{
void LCD_SetUpdateRegion()
{
uint8_t pCols[4] = {0x00, 0x00, 0x01, 0xDF}; /* 0 -> 480 */
uint8_t pRows[4] = {0x00, 0x00, 0x03, 0x3F}; /* 0 -> 800 */

HAL_DSI_LongWrite(&hdsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, OTM8009A_CMD_CASET, pCols);
HAL_DSI_LongWrite(&hdsi, 0, DSI_DCS_LONG_PKT_WRITE, 4, OTM8009A_CMD_PASET, pRows);
}
}

디스플레이는 GRAM의 픽셀 값을 기반으로 픽셀을 설정하여 화면을 줄 단위로 업데이트합니다. 화면 깨짐을 방지하려면 디스플레이가 화면에 픽셀을 그리기 전에 GRAM의 각 픽셀값을 업데이트해야 합니다. 업데이트 속도가 주사선(scan line) 속도만큼 빠른 경우 디스플레이가 업데이트되는 동안 GRAM에 값을 쓸 수 있습니다. 디스플레이는 GRAM 업데이트를 시작해도 안전한 시점에 Tearing Effect 신호를 발신합니다. 높이가 800픽셀인 화면에서 TE 신호를 활성화하고 디스플레이 마지막 줄로 설정하는 코드는 다음과 같습니다.

  /**
* 주사선 800에서 TE 요청.
*/
void LCD_ReqTear(void)
{
static uint8_t ScanLineParams[2];

uint16_t scanline = 800;
ScanLineParams[0] = scanline >> 8;
ScanLineParams[1] = scanline & 0x00FF;

HAL_DSI_LongWrite(&hdsi, LCD_OTM8009A_ID, DSI_DCS_LONG_PKT_WRITE, 2, 0x44, ScanLineParams);
// set_tear_on
HAL_DSI_ShortWrite(&hdsi, LCD_OTM8009A_ID, DSI_DCS_SHORT_PKT_WRITE_P1, 0x35, 0x00);
}

TE 신호가 LTDC를 통한 전송을 시작하기 위해 사용할 수 있는 인터럽트를 트리거합니다. 전송이 완료되면 다른 인터럽트가 트리거됩니다. 이 예제에서는 이중 프레임 버퍼를 사용하여 하나의 버퍼에 그리기를 활성화하는 동시에 다른 버퍼를 GRAM으로 전송합니다.

  void HAL_DSI_TearingEffectCallback(DSI_HandleTypeDef* hdsi)
{
GPIO::set(GPIO::VSYNC_FREQ);
HAL::getInstance()->vSync();
OSWrappers::signalVSync();

if (HAL::getInstance())
{
// 작업이 예약되기를 기다리지 않고 프레임 버퍼를 즉시 스와핑합니다.
// 참고: 작업도 깨어날 때 스와핑되지만 해당 연산은 보호되며
// 이미 스와핑된 경우 어떠한 영향도 주지 않습니다.
HAL::getInstance()->swapFrameBuffers();
}

// 전송 프레임 버퍼 데이터
HAL_DSI_Refresh(hdsi);
}//

void HAL_DSI_EndOfRefreshCallback(DSI_HandleTypeDef* hdsi)
{
// LTDC 기본 주소가 새로운 프레임 버퍼를 가리킴:
__HAL_DSI_WRAPPER_DISABLE(hdsi);
LTDC_LAYER(&hltdc, 0)->CFBAR = ((uint32_t)currFbBase);
__HAL_LTDC_RELOAD_IMMEDIATE_CONFIG(&hltdc);
__HAL_DSI_WRAPPER_ENABLE(hdsi);

GPIO::clear(GPIO::VSYNC_FREQ);
}