MIPI-DSI 명령 모드
이 섹션에서는 명령 모드용 MIPI DSI 인터페이스를 구성하는 방법과 TouchGFX Generator에서 이 구성을 사용하는 방법에 대해 설명합니다. 이 문서에서 사용되는 예제는 16비트 RGB565 프레임 버퍼 형식에 대한 것으로, 일반적으로 STM32CubeMX와 코드 예제에서 다음과 같은 구성 요소들을 살펴봅니다
- LTDC IP
- DSI-HOST IP
- TouchGFX Generator
STM32CubeMX - 하드웨어 구성
LTDC
- 모드
Display Type
을 RGB565 (16 bits) - DSI Mode로 설정
- 계층 설정
Number of layers
를 1 layer로 설정Windows Position
,Frame Buffer Line Length
및Frame Buffer Number of Lines
에서 화면 해상도 설정Layer 0 - Pixel Format
을 RGB565로 설정Layer 0 - Alpha constant for blending
을 255로 설정
- NVIC 설정
LTDC global interrupt
와LTDC global error interrupt
모두 사용하지 않으므로 비활성화해야 합니다.
DSIHOST 구성
- 모드
DSIHost
를 _*Adapted Command Mode with TE Pin**으로 설정
- 디스플레이 인터페이스
Color Coding
을 RGB565 (16 bits) - DSI mode로 설정Maximum Command Size
를 디스플레이 폭에 맞는 숫자로 설정The Refresh of the Display Frame Buffer is Triggered
를 manually by Enabling the LTDC로 설정- 나머지 구성은 선택한 LCD HW에 따라 다릅니다
- NVIC 설정
DSI global interrupt
활성화
STM32CubeMX - TouchGFX Generator
- 모드
- Graphics Application 활성화
- TouchGFX Generator
Display / Interface
를 Parallel RGB (LTDC)로 설정(여전히 애플리케이션이 통신해야 하는 컨트롤러이기 때문)Application Tick Source
를 Custom으로 설정
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)
{
...
/* USER CODE BEGIN LTDC_Init 2 */
__HAL_LTDC_DISABLE(&hltdc);
HAL_DSI_Start(&hdsi);
// Specific LCD controller's initialization code
...
// Turn display off until first frame
HAL_DSI_ShortWrite(&hdsi, 0, DSI_DCS_SHORT_PKT_WRITE_P1, DSI_SET_DISPLAY_OFF, 0x00);
__HAL_LTDC_ENABLE(&hltdc);
/* USER CODE END 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 크기를 전체 화면 영역으로 설정할 수 있습니다.
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 신호를 활성화하고 디스플레이 마지막 줄로 설정하는 코드는 다음과 같습니다.
/**
* Request TE at scanline 800.
*/
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())
{
// Swap frame buffers immediately instead of waiting for the task to be scheduled in.
// Note: task will also swap when it wakes up, but that operation is guarded and will not have
// any effect if already swapped.
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);
}