Ngày 3: GPIO input (Polling)

Bước 1: Tạo project, nếu bạn đã quên cách khởi tạo project, hãy xem lại Ngày 2 và Ngày 1

Bước 2: Cấu hình GPIO trong syscfg, ở đây mình đặt tên pin là Button_1 và chọn hardware là BTN-1 (Left) trên kit. Chọn Pull Up. Với Pull Up mode, chân đọc nút nhấn sẽ được kéo lên mức cao và khi nhấn, nút sẽ đưa về mức thấp. Trạng thái của nút nhấn sẽ được vi điều khiển đọc dựa trên việc đọc mức điện áp. Nhớ bấm CTRL + S để save syscfg

Bước 3: Viết đoạn chương trình này vào

#include <unistd.h>

#include <stdint.h>

#include <stddef.h>

  /* Driver Header files */

#include <ti/drivers/GPIO.h>

  // #include <ti/drivers/I2C.h>

  // #include <ti/drivers/SPI.h>

  // #include <ti/drivers/Watchdog.h>

  /* Driver configuration */

#include "ti_drivers_config.h"

  /*

   *  ======== mainThread ========

   */

void *mainThread(void *arg0)

  {

      volatile uint8_t button_state = 1;

      /* Call driver init functions */

      GPIO_init();

      // I2C_init();

      // SPI_init();

      // Watchdog_init();

      while (1)

      {

          button_state = GPIO_read(Button_1);

          if(button_state == 1)

          {

              GPIO_write(LED_RED, 0);

          }

          else if(button_state == 0)

          {

              GPIO_write(LED_RED, 1);

          }

      }

}

Giải thích code: Đoạn code này sẽ đọc trạng thái của nút nhấn Button_01 thông qua hàm GPIO_read(Button_1)và lưu vào biến button_state, khi nút không nhấn button_state = 1, đèn sẽ tắt và ngược lại, khi nhấn button_state = 0, đèn sáng.

Tăng độ khó lên 1 chút, yêu cầu đổi thành khi bấm nút, đèn sẽ đổi trạng thái (tắt -> sáng, sáng -> tắt).

Theo logic thông thường ta sẽ viết code như sau.

#include <unistd.h>

#include <stdint.h>

#include <stddef.h>

  /* Driver Header files */

#include <ti/drivers/GPIO.h>

  // #include <ti/drivers/I2C.h>

  // #include <ti/drivers/SPI.h>

  // #include <ti/drivers/Watchdog.h>

  /* Driver configuration */

#include "ti_drivers_config.h"

  /*

   *  ======== mainThread ========

   */

void *mainThread(void *arg0)

  {

      volatile uint8_t button_state = 1;

      /* Call driver init functions */

      GPIO_init();

      // I2C_init();

      // SPI_init();

      // Watchdog_init();

      while (1)

      {

          button_state = GPIO_read(Button_1);

          if (button_state == 0)

          {

              GPIO_toggle(LED_RED);

          }

      }

}

Tuy nhiên khi chạy thử ta sẽ thấy LED không hề nháy theo ý chúng ta muốn, việc bật tắt sẽ tương đối ngẫu nhiên, thi thoảng sẽ chạy đúng, thi thoảng sẽ sai. Vấn đề ở đây chính là vi điều khiển sẽ chạy rất nhanh giả sử khi nhấn nút 0.1s, vi điều khiển chạy qua vòng lặp 1000 lần, đồng nghĩa với nó đọc được 1000 lần trạng thái nút nhấn -> thực hiện 1000 lần đảo trạng thái LED làm đèn chớp tắt loạn xì ngầu. Để khắc phục điều này ta cần khóa điều kiện cho nút nhấn. Khi nhấn tín hiệu được kéo từ mức cao về mức thấp, rõ ràng sẽ có vô số trạng thái 0 nhưng chỉ có duy nhất 1 trạng thái chuyển từ 1 sang 0. Do đó ta sẽ bắt trạng thái này để xác định nút có được nhấn hay không.

Sửa đoạn code lại như sau

#include <unistd.h>

#include <stdint.h>

#include <stddef.h>

  /* Driver Header files */

#include <ti/drivers/GPIO.h>

  // #include <ti/drivers/I2C.h>

  // #include <ti/drivers/SPI.h>

  // #include <ti/drivers/Watchdog.h>

  /* Driver configuration */

#include "ti_drivers_config.h"

  /*

   *  ======== mainThread ========

   */

void *mainThread(void *arg0)

  {

      volatile uint8_t button_state = 1;

      volatile uint8_t pre_button_state = 1;

      /* Call driver init functions */

      GPIO_init();

      // I2C_init();

      // SPI_init();

      // Watchdog_init();

      while (1)

      {

          button_state = GPIO_read(Button_1);

          if (button_state == 0 && pre_button_state == 1)

          {

              GPIO_toggle(LED_RED);

          }

          pre_button_state = button_state;

          usleep(10000);

      }

}

Giải thích code: Ta sẽ kiểm tra trạng thái bằng cách xét nếu trước đó nút nhấn chưa nhấn và hiện tại nút nhấn được nhấn, khi đó chương trình sẽ thực hiện hàm GPIO_toggle(LED_RED) bên trong điều kiện, sau đó ta cập nhật pre_button_state thành button_state, lúc này chương trình sẽ không nhảy vào hàm GPIO_toggle(LED_RED) nữa cho đến khi nút được nhả, biến button_state được cập nhật lại và khi này sẽ có sự khác nhau giữa button_state và pre_button_state, chương trình tiếp tục nhảy vảo hàm GPIO_toggle(LED_RED);

Mặc dù cách này có thể cho ra đúng kết quả tuy nhiên việc đọc trạng thái nút nhấn liên tục bằng vòng lặp làm lãng phí hiệu năng của vi điều khiển khi nó liên tục phải chờ đọc trạng thái của nút nhấn trong vòng lặp. Ở bài học tiếp theo chúng ta sẽ tiếp cận với phương pháp đọc nút nhấn phổ biến hơn đó là dùng ngắt – giúp xác định trạng thái khi có sự kiện xảy ra mà không cần phải đứng chờ trong vòng lặp liên tục

Leave a Comment