[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 라이브러리를 이용하여 좀 더 화려한 모양으로 되어 있고 터치까지 지원되는 데모를 점검해 보겠습니다.
틀린 부분이나 질문은 댓글 달아주세요.
즐거운 하루 보내세요. 감사합니다.
댓글