[TH] ESP-IDF Ep.3 : GPIO Output

บทความนี้กล่าวถึงการตั้งค่า GPIO และการส่งสถานะดิจิทัล 0 หรือ 1 ไปยังพอร์ต โดยเริ่มจากการสั่งให้หลอดแอลอีดีสว่างและดับ โดยเชื่อมต่อกับวงจรแอลอีดีภายนอกบอร์ดดังภาพที่ 1

ภาพที่ 1 การทดลองขับหลอดแอลอีดีจาก esp32

โครงสร้างของโครงงาน

โครงสร้างของโครงงานของ ESP-IDF เป็นดังภาพที่ 2 คือ ในไดเร็กทอรีหรือโฟลเดอร์ของโครงงานจะมีไฟล์ CMakeList.txt และ sdkconfig กับไดเร็กทอรีชื่อ main สำหรับเก็บรหัสต้นฉบับของโครงงาน โดยในไดเร็กทอรีดังกล่าวมีไฟล์ภาษา C และ CMakeLists.txt

ภาพที่ 2 โครงสร้างโครงงาน

จากโครงสร้างในภาพที่ 2 ต้องสร้างโค้ดของไฟล์ CMakeLists.txt ดังนี้ ซึ่งเนื้อหาในโค้ดได้กำหนดรุ่นขั้นต่ำของโปรแกรม cmake และกำหนดค่าการใช้งานของ cmake เบื้องต้นตามจ้นฉบับที่มากับ ESP-IDF พร้อมทั้งตั้งชื่อโครงงานเป็น ep03

cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ep03)

สิ่งที่เขียน main/CMakeLists.txt เป็นดังงต่อไปนี้ เพื่อกำหนดรายการไฟล์ที่จะต้องคอมไพล์ ซึ่งกำหนดไว้เป็น main.c และกำหนดไดเร็กทอรีที่เก็บไฟล์ส่วนหัวเอาไว้เป็นค่าว่างซึ่งหมายถึงที่เดียวกับ main.c หรือในไดเร็กทอรี main

idf_component_register(SRCS "main.c"
                    INCLUDE_DIRS "")

เมื่อสร้างโครงสร้างได้เหมือนดังภาพที่ 2 ให้สั่งเลือก target ของระบบเป็น ESP32 ดังนี้

idf.py set-target esp32

ส่วน sdkconfig เกิดจากการเรียกใช้คำสั่งต่อไปนี้ idf.py menuconfig

idf.py menuconfig

จากหน้าจอกำหนดการตั้งค่าให้เข้าไปที่ Component Config –> FreeRTOS และกำหนด Tick rate (Hz) เป็น 1000 ดังภาพที่ 3 หลังจากนั้นบันทึกและออกจากการตั้งค่า

ภาพที่ 3 ตั้งค่า Tick rate (Hz)

คำสั่ง

การใช้งาน GPIO จะต้องนำเข้าไฟล์ส่วนหัวชื่อ gpio.h ซึ่งอยู่ในโฟลเดอร์ย่อย driver ของ ESP-IDF ซึ่งสามารถนำเข้าดั้วยคำสั่ง include ดังนี้

#include <driver/gpio.h>

การเลือกใช้ขาต้องระบุด้วยคำสั่งต่อไปนี้

gpio_pad_select_gpio( หมายเลขขา )

คำสั่งสำหรับกำหนดหน้าที่การทำงานของขา มีรูปแบบดังนี้ โดยโหมดของขาเป็น GPIO_MODE_INPUT หรือ GPIO_MODE_OUTPUT

gpio_set_direction( หมายเลขขา, โหมดของขา )

การกำหนดสถานะของแรงดันดิจิทัลให้กับขามีรูปแบบของคำสั่งดังนี้

gpio_set_level( หมายเลขขา, ระดับแรงดันดิจิทัล )

สำหรับการหน่วงเวลาจะต้องเรียกใช้ชุดคำสั่งการหน่วงเวลาจาก FreeRTOS ด้วยคำสั่งการหน่วงเวลาต่อไปนี้ โดยการเข้าถึงค่า Tick rate (Hz) ที่กำหนดไว้ในขั้นตอน menuconfig สามารถเรียกได้จากค่าคงที่ชื่อ portTICK_PERIOD_MS

vTaskDelay( ระยะเวลาที่ต้องการหน่วงเวลา )

กรณีที่ต้องการใช้งาน GPIO แบบอื่น ๆ สามารถเข้าไปอ่านบทความต่าง ๆ ดังต่อไปนี้เพิ่มเติม

  1. นำออกข้อมูลสัญญาณดิจิทัล
  2. นำเข้าสัญญาณดิจิทัล
  3. นำเข้าสัญญาณแอนาล็อก
  4. ำออกสัญญาณแอนาล็อก

ตัวอย่างโปรแกรม

วงจรแอลอีดีที่ใช้ในการทดลองครั้งนี้เป้นดังภาพที่ 4 และ 5 อุุปกรณ์ที่ใช้ในการทดลองได้แก่

  1. บอร์ด esp32
  2. บอร์ดทดลอง
  3. แอลอีดี
  4. ตัวต้านทาน
ภาพที่ 4 วงจรขับหลอดแอลอีดี
ภาพที่ 5 การต่อวงจรตามภาพที่ 4

จากภาพที่ 5 จะพบว่าได้เชื่อมต่อขา GPIO13 เข้ากับขาของแอลอีดีฝั่งที่เป็นส่วนของ Vcc นั่นหมายความว่าถ้าส่งข้อมูลเป็น 1 จะทกให้หลอดติด และถ้าส่งเป็น 0 จะทำให้หลอดดับ ดังตัวอย่างโปรแกรมต่อไปนี้

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define LED_PIN 13 // GPIO13

void app_main(void)
{  
  printf("Ep.02 GPIO"); 
  gpio_pad_select_gpio(LED_PIN);
  gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);

  while(1) {
    gpio_set_level(LED_PIN, 0);
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_PIN, 1 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
  }
}

คอมไพล์และอัพโหลด

ทำการคอมไพล์ด้วยคำสั่งต่อไปนี้จะได้รายงานผลดังภาพที่ 6 และ 7

idf.py build

ภาพที่ 6 การ build
ภาพที่ 7 ผลจากการ build

ขั้นตอนสุดท้ายคือสั่งอัพโหลดลงชิพด้วยคำสั่งต่อไปนี้ และจะได้รายงานการทำงานตามภาพที่ 8 และ 9

idf.py -p /dev/ttyUSB0 flash

ภาพที่ 8 การสั่ง flash
ภาพที่ 9 ตัวอย่างผลลัพธ์เมื่อ flash เสร็จสิ้น

จากตัวอย่างโปรแกรมมีผลการทำงานที่หลอดแอลอีดีดังภาพที่ 10 และ 11 สลับกันไป

ภาพที่ 10 ตัวอย่างผลลัพธ์เมื่อหลอดแอลอีดีดับ
ภาพที่ 11 ตัวอย่างเมื่อหลอดแอลอีดีสว่าง

สำหรับตัวอย่างการสั่งงานกับบอร์ด ESP32-C3 เพื่อให้หลอด LED-RGB ติดสลับกันไป ให้เปลี่ยนโค้ดโปรแกรมเป็นดังนี้ และจะได้ผลลัพธ์ดังภาพที่ 12

#include <stdio.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#define LED_R_PIN 3
#define LED_G_PIN 4
#define LED_B_PIN 5

void app_main(void)
{  
  printf("C3 LEDs"); 
  gpio_pad_select_gpio(LED_R_PIN);
  gpio_set_direction(LED_R_PIN, GPIO_MODE_OUTPUT);
  gpio_pad_select_gpio(LED_G_PIN);
  gpio_set_direction(LED_G_PIN, GPIO_MODE_OUTPUT);
  gpio_pad_select_gpio(LED_B_PIN);
  gpio_set_direction(LED_B_PIN, GPIO_MODE_OUTPUT);

  while(1) {
    gpio_set_level(LED_R_PIN, 1);
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_R_PIN, 0 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_G_PIN, 1);
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_G_PIN, 0 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_B_PIN, 1);
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_B_PIN, 0 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_R_PIN, 1 );
    gpio_set_level(LED_G_PIN, 1 );
    gpio_set_level(LED_B_PIN, 0 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_R_PIN, 0 );
    gpio_set_level(LED_G_PIN, 1 );
    gpio_set_level(LED_B_PIN, 1 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_R_PIN, 1 );
    gpio_set_level(LED_G_PIN, 0 );
    gpio_set_level(LED_B_PIN, 1 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_R_PIN, 1 );
    gpio_set_level(LED_G_PIN, 1 );
    gpio_set_level(LED_B_PIN, 1 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
    gpio_set_level(LED_R_PIN, 0 );
    gpio_set_level(LED_G_PIN, 0 );
    gpio_set_level(LED_B_PIN, 0 );
    vTaskDelay( 500/portTICK_PERIOD_MS );
  }
}

สิ่งที่ห้ามลืมคือ ต้องเปลี่ยน target เป็น esp32c3 ด้วยคำสั่งต่อไปนี้ก่อนดำเนินการอัพโหลดโปรแกรม

idf.py set-target esp32c3

ภาพที่ 12 ตัวอย่างผลลัพธ์เมื่อรันบนบอร์ด ESP32-C3

สรุป

จากบทความนี้จะพบว่าการเขียนโปรแกรมด้วย ESP-IDF ค่อนข้างมีความซับซ้อนเนื่องจากเครื่องมืออยู่ในรูปแบบโหมดตัวอักษร (แต่ถือว่าสะดวกสำหรับนักพัฒนาที่ใช้ระบบปฏิบัติการลิกนุกซ์) แต่ถ้าพิจารณาขั้นตอนการเขียนโปรแกรมจะมีความสะดวกเพราะสามารถเขียนกำหนดและสั่งงานได้เหมือนกับการเขียนด้วย Arduino แต่คำสั่งนั้นแตกต่างกัน

การกำหนดหรือตั้งค่าไมโครคอนโทรลเลอร์จะต้องไปทำให้ idf.py menuconfig เพื่อตั้งค่าความถี่หรืออื่น ๆ ก่อนเสมอเพื่อให้การทำงานถูกต้องตามที่ต้องการ ซึ่งก่อนเข้า menuconfig ต้องกำหนด target ให้ถูกต้องก่อน ซึ่งได้แก่ esp32 esp32s2 esp32c3 และ esp32s3 โดยในบทความนี้เป็น esp32

สุดท้ายขอให้สนุกกับการเขียนโปรแกรมครับ

ท่านใดต้องการพูดคุยสามารถคอมเมนท์ได้เลยครับ

(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-07-27, 2021-10-01, 2021-12-10