본문 바로가기
ESP32

[강좌]ESP32 4.3" TFT-LCD HMI - 5. TFT 예제 살펴보기 2(Explore Examples 2)

by 소나무기운 2023. 3. 30.
반응형

[2023/03/29] First Start.

소나무 기운 ,  전자제품 개발/생산

메인ESP32 4.3" TFT-LCD HMI - 5. TFT 예제 살펴보기2(Explore Examples2)

오늘은 나머지 예제 두개를 더 살펴 보겠습니다. 4번 강좌에서 사용한 라이브러리와 환경을 그대로 사용하므로 어려울 것은 없습니다.

예제 소스를 살펴보는 정도로 편하게 읽어보시면 되겠습니다.

예제 하나는 CLOCK이고, 또 하나는 PDQgraphicstest가 되겠습니다.

제목과 비슷한 내용일듯 합니다.

같이 한번 보시죠. 

 

Examples 3_3-2_TFT_CLOCK

원형의 바늘시계를 구현합니다.

시,분,초 바늘이 있고 1초마다 한번씩 초를 증가하고 그에 따른 분, 시도 변경을 해 줍니다.

그리고 초바늘의 위치를 계산하여 새로운 Pixel로 이동이 될때마다 화면을 갱신합니다.

 

즉 1초 마다 초바늘이 한번 움직이는 것이 아니라 부드럽게 움직이는 초바늘을 구현하였습니다.

 

우선 동작하는 영상을 한번 보시죠.

 

< 시계 움직이는 동영상 >

 

setup()함수 설명

void setup(void)
{
    // gfx 객체의 begin 함수를 호출하여 LCD 디스플레이를 초기화합니다.
    gfx->begin();
    // fillScreen 함수를 호출하여 LCD를 배경색으로 채웁니다.
    gfx->fillScreen(BACKGROUND);

#ifdef TFT_BL
    pinMode(TFT_BL, OUTPUT);
    digitalWrite(TFT_BL, HIGH);
#endif

    // init LCD constant
    // LCD 크기 정보를 초기화합니다.
    // 만약 세로 크기가 가로 크기보다 크다면, 중앙 좌표는 가로 크기의 반으로 설정합니다.
    // 반대의 경우는 세로 크기의 반으로 설정합니다.
    // 시침, 분침, 초침의 길이와 표시할 표시물의 길이를 계산하고, 
    // 이를 저장하기 위해 동적 메모리 할당을 수행합니다.
    w = gfx->width();
    h = gfx->height();
    if (w < h)
    {
        center = w / 2;
    }
    else
    {
        center = h / 2;
    }
    hHandLen = center * 3 / 8;
    mHandLen = center * 2 / 3;
    sHandLen = center * 5 / 6;
    markLen = sHandLen / 6;
    cached_points = (int16_t *)malloc((hHandLen + 1 + mHandLen + 1 + sHandLen + 1) * 2 * 2);

    // Draw 60 clock marks
    // draw_round_clock_mark 함수를 호출하여 시계 바늘 표시를 그립니다.
    draw_round_clock_mark(
    // draw_square_clock_mark(
        center - markLen, center,
        center - (markLen * 2 / 3), center,
        center - (markLen / 2), center);

    // 현재 시간을 TIME 매크로를 사용하여 hh(시), mm(분), ss(초) 변수에 저장합니다.
    hh = conv2d(__TIME__);
    mm = conv2d(__TIME__ + 3);
    ss = conv2d(__TIME__ + 6);

    // millis 함수를 사용하여 다음 1초 단위의 시간을 계산하고, targetTime 변수에 저장합니다.
    targetTime = ((millis() / 1000) + 1) * 1000;
}

그래픽 라이브러리를 초기화 하고, digitalWrite(TFT_BL, HIGH);는 LCD Back-light를 켜는 동작입니다.

 

 

loop()함수 설명

// SIXTIETH_RADIAN: 1분에 해당하는 각도(라디안)
// RIGHT_ANGLE_RADIAN: 90도에 해당하는 각도(라디안)
// SIXTIETH: 1분에 해당하는 각도(도)
// TWELFTH_RADIAN: 1시간에 해당하는 각도(라디안)
// TWELFTH: 1시간에 해당하는 각도(도)

void loop()
{
    // millis 함수를 사용하여 현재 시간을 계산합니다.
    unsigned long cur_millis = millis();
    
    // targetTime에 1초(1000 밀리초)를 더한 값이 현재 millis 값보다 크거나 같으면, 
    // ss(초) 값을 1 증가시킵니다. 이 때 ss 값이 60이 되면, mm(분) 값을 1 증가시키고, 
    // mm 값이 60이 되면, hh(시) 값을 1 증가시킵니다. hh 값이 24가 되면, 
    // 0으로 초기화합니다.
    if (cur_millis >= targetTime)
    {
        targetTime += 1000;
        ss++; // Advance second
        if (ss == 60)
        {
            ss = 0;
            mm++; // Advance minute
            if (mm > 59)
            {
                mm = 0;
                hh++; // Advance hour
                if (hh > 23)
                {
                    hh = 0;
                }
            }
        }
    }

    // Pre-compute hand degrees, x & y coords for a fast screen update
    // 현재 시각을 바탕으로 초침, 분침, 시침의 위치를 계산하고, 
    // 그래픽 객체(gfx)를 사용하여 LCD에 그립니다.
    sdeg = SIXTIETH_RADIAN * ((0.001 * (cur_millis % 1000)) + ss); // 0-59 (includes millis)
    nsx = cos(sdeg - RIGHT_ANGLE_RADIAN) * sHandLen + center;
    nsy = sin(sdeg - RIGHT_ANGLE_RADIAN) * sHandLen + center;
    // 이전 위치 정보와 비교하여, 바늘 위치가 변경되었을 때에만 화면을 갱신합니다.
    if ((nsx != osx) || (nsy != osy))
    {
        mdeg = (SIXTIETH * sdeg) + (SIXTIETH_RADIAN * mm); // 0-59 (includes seconds)
        hdeg = (TWELFTH * mdeg) + (TWELFTH_RADIAN * hh);   // 0-11 (includes minutes)
        mdeg -= RIGHT_ANGLE_RADIAN;
        hdeg -= RIGHT_ANGLE_RADIAN;
        nmx = cos(mdeg) * mHandLen + center;
        nmy = sin(mdeg) * mHandLen + center;
        nhx = cos(hdeg) * hHandLen + center;
        nhy = sin(hdeg) * hHandLen + center;

        // redraw hands
        redraw_hands_cached_draw_and_erase();

		// 새로운 위치 정보를 이전 위치 정보로 저장합니다.
        ohx = nhx;
        ohy = nhy;
        omx = nmx;
        omy = nmy;
        osx = nsx;
        osy = nsy;

        delay(1);
    }
}

1초마다 한번씩 초, 분, 시를 변경합니다.

현재 바늘의 위치를 계산하여 이전 위치와 다를 경우에는 화면에 바늘을 지우고 다시 그립니다.

 

기타 함수 표시

// 원형 시계의 외곽 모양을 그립니다. (기본)
draw_round_clock_mark();

// 사각 시계의 외곽 모양을 그립니다. (미사용)
draw_square_clock_mark();

// 시분초 시계 바늘을 기존것을 지우고 새롭게 그립니다.
redraw_hands_cached_draw_and_erase();

// 기존 선을 지우고 새로운 선으로 그립니다.
draw_and_erase_cached_line();

// 원하는 위치에 색을 지정하여 점을 찍습니다.
write_cache_pixel();

 

 

 

Examples 3_3-3_TFT_PDQgraphicstest

이 예제는 gfx 라이브러리를 이용하여 pixel, line, rectangle, circle, text등을 출력하여 얼마나 빠르게 작업을 수행할 수 있는지 시험하는 예제입니다. 각각의 테스트를 끝내고 나면 각각의 테스트가 얼마나 오래 걸렸는지를 us시간으로 표시하여 화면에 출력해 줍니다.

동작하는 화면을 보시죠.

 

 

setup()함수 설명

void setup()
{
  // 시리얼 통신 시작, 통신 속도 115200 bps로 설정
  Serial.begin(115200);
  Serial.println("Arduino_GFX library Test!");

  // 그래픽 라이브러리 초기화
  gfx->begin();

  w = gfx->width();
  h = gfx->height();
  // 너비와 높이 중 작은 값을 n에 저장
  n = min(w, h);
  n1 = n - 1;
  cx = w / 2;
  cy = h / 2;
  cx1 = cx - 1;
  cy1 = cy - 1;
  cn = min(cx1, cy1);
  cn1 = cn - 1;
  // 텍스트 사이즈
  tsa = ((w <= 176) || (h <= 160)) ? 1 : (((w <= 240) || (h <= 240)) ? 2 : 3); // text size A
  tsb = ((w <= 240) || (h <= 220)) ? 1 : 2;                                    // text size B
  tsc = ((w <= 220) || (h <= 220)) ? 1 : 2;                                    // text size C
  ds = (w <= 160) ? 9 : 12;                                                    // digit size

  // LCD Back light ON
#ifdef TFT_BL
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH);
#endif
}

 

loop()함수 설명

void loop(void)
{
  Serial.println(F("Benchmark\tmicro-secs"));

  int32_t usecFillScreen = testFillScreen();
  serialOut(F("Screen fill\t"), usecFillScreen, 100, true);

  // 순차적으로 시험을 진행합니다.
  int32_t usecText = testText();
  serialOut(F("Text\t"), usecText, 3000, true);

  int32_t usecPixels = testPixels();
  serialOut(F("Pixels\t"), usecPixels, 100, true);

  int32_t usecLines = testLines();
  serialOut(F("Lines\t"), usecLines, 100, true);

  int32_t usecFastLines = testFastLines();
  serialOut(F("Horiz/Vert Lines\t"), usecFastLines, 100, true);

  int32_t usecFilledRects = testFilledRects();
  serialOut(F("Rectangles (filled)\t"), usecFilledRects, 100, false);

  int32_t usecRects = testRects();
  serialOut(F("Rectangles (outline)\t"), usecRects, 100, true);

  int32_t usecFilledTrangles = testFilledTriangles();
  serialOut(F("Triangles (filled)\t"), usecFilledTrangles, 100, false);

  int32_t usecTriangles = testTriangles();
  serialOut(F("Triangles (outline)\t"), usecTriangles, 100, true);

  int32_t usecFilledCircles = testFilledCircles(10);
  serialOut(F("Circles (filled)\t"), usecFilledCircles, 100, false);

  int32_t usecCircles = testCircles(10);
  serialOut(F("Circles (outline)\t"), usecCircles, 100, true);

  int32_t usecFilledArcs = testFillArcs();
  serialOut(F("Arcs (filled)\t"), usecFilledArcs, 100, false);

  int32_t usecArcs = testArcs();
  serialOut(F("Arcs (outline)\t"), usecArcs, 100, true);

  int32_t usecFilledRoundRects = testFilledRoundRects();
  serialOut(F("Rounded rects (filled)\t"), usecFilledRoundRects, 100, false);

  int32_t usecRoundRects = testRoundRects();
  serialOut(F("Rounded rects (outline)\t"), usecRoundRects, 100, true);

#ifdef CANVAS
  uint32_t start = micros_start();
  gfx->flush();
  int32_t usecFlush = micros() - start;
  serialOut(F("flush (Canvas only)\t"), usecFlush, 0, false);
#endif

  Serial.println(F("Done!"));

  uint16_t c = 4;
  int8_t d = 1;
  for (int32_t i = 0; i < h; i++)
  {
    gfx->drawFastHLine(0, i, w, c);
    c += d;
    if (c <= 4 || c >= 11)
    {
      d = -d;
    }
  }

  gfx->setCursor(0, 0);

  gfx->setTextSize(tsa);
  gfx->setTextColor(MAGENTA);
  gfx->println(F("Arduino GFX PDQ"));

  if (h > w)
  {
    gfx->setTextSize(tsb);
    gfx->setTextColor(GREEN);
    gfx->print(F("\nBenchmark "));
    gfx->setTextSize(tsc);
    if (ds == 12)
    {
      gfx->print(F("   "));
    }
    gfx->println(F("micro-secs"));
  }

  // 측정 결과를 화면에 표시한다.
  gfx->setTextSize(1);
  printnice(F("Screen fill "), usecFillScreen);
  printnice(F("Text        "), usecText);
  printnice(F("Pixels      "), usecPixels);
  printnice(F("Lines       "), usecLines);
  printnice(F("H/V Lines   "), usecFastLines);
  printnice(F("Rectangles F"), usecFilledRects);
  printnice(F("Rectangles  "), usecRects);
  printnice(F("Triangles F "), usecFilledTrangles);
  printnice(F("Triangles   "), usecTriangles);
  printnice(F("Circles F   "), usecFilledCircles);
  printnice(F("Circles     "), usecCircles);
  printnice(F("Arcs F      "), usecFilledArcs);
  printnice(F("Arcs        "), usecArcs);
  printnice(F("RoundRects F"), usecFilledRoundRects);
  printnice(F("RoundRects  "), usecRoundRects);

  if ((h > w) || (h > 240))
  {
    gfx->setTextSize(tsc);
    gfx->setTextColor(GREEN);
    gfx->print(F("\nBenchmark Complete!"));
  }

#ifdef CANVAS
  gfx->flush();
#endif

  delay(60 * 1000L);
}

여러가지 테스트를 진행한 후 결과를 화면에 표시해 줍니다.

진행하는 테스트의 종류로는 fill, line, text, pixel, Rectangles, Circles등등을 시험하여 각 시험마다 걸리는 시간을 us의 시간으로 표시해 줍니다.

 

기타 함수 설명

// 시리얼로 검사 결과를 출력합니다.
serialOut();

// 화면에 검사 결과를 출력합니다.
printnice();

// 각각 gfx 라이브러리를 이용한 테스트를 진행합니다.
testFillScreen();
testText();
testPixels();
testLines();
...
...
testRoundRects();

< 기타 함수 설명 >

 

 

 

Arduino-IDE 설정 값

아두이노 IDE의 설정 값 상태를 다시한번 보여드리겠습니다. 참고하세요.

< 설정값 상태 표시 >

 

 

 

마무리

두가지 예제 프로그램을 살펴보았습니다. 패러럴로 LCD의 데이터를 갱신하면서 속도가 아주 빠릅니다. 또 ESP32-s3의 경우 내부 플래시가 16MByte로 커서 LCD를 이용한 제품으로 아주 적격입니다. 이 예제를 참고하면 한가지 화면으로 구성된 아주 빠른 반응성이 있는 화면이 필요할 경우 사용하면 좋겠습니다.

 

 

 

참고문헌

마무리5

이렇게 몇가지 예제를 확인해 보았습니다. 이제 실제로 HMI제작시 사용하게 되는 lvgl 라이브러리를 이용하여 좀 더 화려한 모양으로 되어 있고 터치까지 지원되는 데모를 점검해 보겠습니다.

 

 
 

 

 

틀린 부분이나 질문은 댓글 달아주세요.

즐거운 하루 보내세요. 감사합니다.

 

 

반응형

댓글