ESP32/ESP32-WROOM-32

ESP-WROOM-32 보드 사용하기 5편(LCD 터치)

원원 2024. 6. 22. 20:58

안녕하세요. 이전 4편에서 LCD에 화면 출력을 했었는데 이번에는 터치기능을 추가해보겠습니다.
ESP32-2432S028보드에는 터치센서가 달려있습니다. (XPT2046)

먼저, XPT2046 터치센서의 원리를 간단히 알아보겠습니다. 터치센서는 저항식 터치센서(Resistive Touch Sensor)와 정전식 터치 센서(Capacitive Touch Sensor) 일반적으로 두가지 유형이 있습니다. 저항식 터치센서는 터치를하면 저항값이 변하는걸 인식하는거고, 정전식 터치센서는 정전 용량의 변화를 통해 터치위치를 계산합니다. 
XPT2046은 저항식 터치센서입니다.  아래의 회로도에서 XP,YP,XN,YN핀은 터치패널모듈에 연결됩니다. ESP32 제품에보면 TPM408-2.8이라고 적혀있는게 있는데 이게 터치패널모듈입니다. (빨강네모) 
터치해서 나온값을 SPI통신으로 프로토콜에 맞게 MCU에게 주게됩니다.


이제 기존의 프로젝트에 TOUCH기능을 추가해보겠습니다. 기존의 프로젝트는 4편에 있는 프로젝트입니다.

1. TFT_Touch 라이브러리를  사용하는 프로젝트에 다운받습니다

라이브러리를 추가하고나서 playformio.ini -> lib_deps에서 추가한 라이브러리를 알 수 있습니다


2. 소스코드를 추가합니다
1) 터치 헤더파일을 추가합니다

#include <TFT_Touch.h> //추가

 2) 터치 변수선언, XPT2046와 MCU가 연결된대로 선언해줍니다. IRQ핀은 사용하지않습니다

#define DOUT 39  /* Data out pin (T_DO) of touch screen */ //추가
#define DIN  32  /* Data in pin (T_DIN) of touch screen */ //추가
#define DCS  33  /* Chip select pin (T_CS) of touch screen */ //추가
#define DCLK 25  /* Clock pin (T_CLK) of touch screen */ //추가
TFT_Touch touch = TFT_Touch(DCS, DCLK, DIN, DOUT); //추가

3) 선언되어있는 터치함수의 touched에 touch.Pressed()라고 수정합니다.

void my_touchpad_read( lv_indev_drv_t * indev_driver, lv_indev_data_t * data )
{
    uint16_t touchX = 0, touchY = 0;

    bool touched = touch.Pressed();  //수정

4)touchX, touchY에 값을 넣어줍니다

    if( !touched )
    {
        data->state = LV_INDEV_STATE_REL;
    }
    else
    {
        touchX = touch.X(); //추가
        touchY = touch.Y(); //추가
        data->state = LV_INDEV_STATE_PR;

5) touch.setCal함수는 터치센서의 cal값을 지정해주는것입니다. 디폴트로 해당값으로 사용하고 터치인식이 이상하다면 수정해주면 됩니다. touch.setRotation은 터치방향입니다. tft.setRotation과 동일하게 해줍니다.
tft.invertDisplay는 디스플레이를 반전하는 함수인데, true로해야 squareline에서 UI를 만든것과 동일하게 나옵니다.

    tft.begin();          /* TFT init */
    tft.setRotation( 3 ); /* Landscape orientation, flipped */
    tft.invertDisplay(true); //추가
    touch.setCal(526, 3443, 750, 3377, 320, 240, 3); //추가
    touch.setRotation(3); //추가
 

이렇게 수정하면 터치센서를 사용할수있습니다. 펌웨어를 넣으면 시리얼모니터로 좌표가 나옵니다.


이번에는 버튼을 추가해서 클릭할때마다 숫자를 1씩 증가시켜서 화면에 출력해보겠습니다.
1) 버튼 UI를 만듭니다

2) 버튼을 동작했을때 발생할 이벤트를 선택해줍니다.  Trigger에는 Action 발동조건을 넣어줍니다
Action은 CALL FUNCTION을 선택해서 Trigger했을때 함수를 호출해주게 설정합니다.
Export UI Files를 클릭해서 코드를 만들어줍니다.

3) ui.c에 가보면 아래와같은 함수가 있습니다.
버튼을 클릭했을때 호출되는 함수입니다. 근데 코드가 ui.c에 만들어졌는데 Export UI Files를 할때마다 코드가 갱신되고 main.cpp에있는곳에서 사용하는 변수를 사용하려면 따로 선언을 해줘야하므로 해당함수를 main.cpp로 옮겨서 사용합니다. 

///////////////////// FUNCTIONS ////////////////////
void ui_event_Button1(lv_event_t * e)
{
    lv_event_code_t event_code = lv_event_get_code(e);
    lv_obj_t * target = lv_event_get_target(e);
    if(event_code == LV_EVENT_CLICKED) {
        btn1_func(e);
    }
}

4) 버튼을 클릭했을때 값을 1씩 올라가는 코드를 추가해줍니다.

int val = 0;

void ui_event_Button1(lv_event_t * e)
{
    lv_event_code_t event_code = lv_event_get_code(e);
    lv_obj_t * target = lv_event_get_target(e);
    if(event_code == LV_EVENT_CLICKED) {
        val++;
        lv_label_set_text_fmt(ui_Label1,"val : %d",val);
    }
}


소스코드 commit : add touch function
https://github.com/yhunterr/esp32-2432s028/


이번에는 스톱워치 기능을 넣어보겠습니다.

버튼은 3개 넣었습니다.
START버튼 : 스톱워치시작
STOP 버튼 : 스톱워치중지 (시간 초기화안됨)
RESET 버튼 : 스톱워치중지 and 시간초기화

버튼클릭하면 START/STOP/RESET 상태를 인식시켰고 메인문에서 1초마다 상태를 확인해서 그에맞게 동작시켰습니다. time_format함수를 통해서 시간을 넘겨주면 hh:mm:ss 포맷에 맞게 리턴받았습니다.

unsigned long timer1 = millis();
unsigned long stopWatch_time;
char start_status = -1;
char reset_status = -1;
///////////////////// FUNCTIONS ////////////////////
void ui_event_Button1(lv_event_t * e) //START
{
    lv_event_code_t event_code = lv_event_get_code(e);
    lv_obj_t * target = lv_event_get_target(e);
    if(event_code == LV_EVENT_CLICKED) {
        start_status = 1;
    }

}
void ui_event_Button2(lv_event_t * e) //STOP
{
    lv_event_code_t event_code = lv_event_get_code(e);
    lv_obj_t * target = lv_event_get_target(e);
    if(event_code == LV_EVENT_CLICKED) {
        start_status = 0;
    }
}
void ui_event_Button3(lv_event_t * e) //RESET
{
    lv_event_code_t event_code = lv_event_get_code(e);
    lv_obj_t * target = lv_event_get_target(e);
    if(event_code == LV_EVENT_CLICKED) {
        start_status = 0;
        reset_status = 1;
    }
}

char* time_format(long l) {
    int hours = l / 3600;
    int minutes = (l % 3600) / 60;
    int seconds = l % 60;
    static char formatted_time[9];
    sprintf(formatted_time, "%02d:%02d:%02d", hours, minutes, seconds);
    return formatted_time;
}

void loop()
{
    if(millis() > timer1+1000)
    {
      timer1 = millis();
      if(reset_status == 1)
      {
        Serial.println("RESET");
        stopWatch_time = 0;
        lv_label_set_text_fmt(ui_Label6,"00:00:00");
        reset_status = -1;
      }
      if(start_status == 1)
      {
        char* result = time_format(stopWatch_time);
        Serial.println("START");
        stopWatch_time++;
        lv_label_set_text_fmt(ui_Label6,"%s",result);
      }
      else if(start_status == 0)
      {
        Serial.println("STOP");
        start_status = -1;
      }
      Serial.println(stopWatch_time);
    }
    lv_timer_handler(); /* let the GUI do its work */
    delay(5);
}


소스코드 commit : stop watch
https://github.com/yhunterr/esp32-2432s028/