Bài 2 - Nút nhấn điều khiển LED

 

Chương trình của chúng ta như sau, khi nhấn nút đèn LED sẽ sáng, nhấn nút một lần nữa đèn LED sẽ tắt, cứ thế lặp lại.

Sơ đồ mạch như sau, LED sẽ nối với GPIO12, nút nhấn nối với GPIO13:

 


Giới thiệu về GPIO

Vi điều khiển trong các board mạch tương tác với thế giới bên ngoài thông qua các chân GPIO (General Purpose Input Output Pins). Các chân GPIO được nối với các thiết bị cảm biến để theo dõi, đo đạc môi trường bên ngoài hoặc thiết bị điều khiển đóng ngắt như bật tắt đèn, loa, động cơ… Các chân GPIO có thể là input hoặc output, khi nó là input chúng ta có thể đọc giá trị từ nút nhấn, cảm biến…, khi nó là output nó có thể xuất tín hiệu để bật tắt LED…

ESP8266 có 16 chân GPIO, mỗi chân trên ESP8266 có thể thực hiện nhiều chức năng, từ Function 1 đến Function 4.


Để lựa chọn 1 chức năng ta dùng câu lệnh sau (theo hướng dẫn esp8266-technical_reference_en.pdf): PIN_FUNC_SELECT(PIN_NAME, FUNC)


với PIN_NAME = PERIPHS_IO_MUX+ với tên trong cột Pin Name và FUNC = FUNC+ tên trong cột từ Fuction 1 đến cột Function 4 như hình trên.

              Ví dụ: chân 12 muốn chọn Function 2 cho chức năng I2S ta dùng lệnh:  

                                    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_I2SI_DATA);           

Trong trường hợp chương trình của chúng ta thì chọn Function 4 cho chức năng GPIO12, GPIO13.

PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12);

PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);

Để khai báo GPIO12 là output để xuất tín hiệu cho đèn LED, GPIO13 là input để nhận tín hiệu từ nút nhấn thì làm như sau:

 


                            GPIO_OUTPUT_SET( 12, 0);   //GPIO12 là ouput với mức thấp

                            GPIO_DIS_OUTPUT(13); //GPIO13 là input để nhận tín hiệu từ nút nhấn

 Giới thiệu về ngắt

Để đọc giá trị trên nút nhấn nối với một chân trên ESP8266, giá trị có thể chuyển từ giá trị cao xuống thấp hoặc từ thấp lên cao. Để xác định thời điểm thay đổi giá trị như vậy xảy ra, chúng ta có thể liên tục thăm dò giá trị và phát hiện thay đổi của nút nhấn. Tuy nhiên đây không phải là giải pháp tốt nhất vì một số lý do. Đầu tiên, chúng ta phải liên tục kiểm tra xem một giá trị có thay đổi hay không (nút nhấn có nhấn hay không). Thứ hai, sẽ có độ trễ từ thời điểm sự kiện xảy ra (nhấn nút) đến thời điểm chúng ta kiểm tra. Thứ ba, có thể hoàn toàn bỏ lỡ một sự thay đổi tín hiệu nếu thời gian của sự thay đổi đó ngắn.

Giải pháp cho tất cả những vấn đề này là khái niệm về ngắt. Ngắt trong cuộc sống tương tự như chuông cửa ở nhà của bạn. Nếu không có chuông cửa, bạn sẽ phải liên tục thăm dò xem có ai ở trước cửa nhà bạn không. Điều này làm lãng phí thời gian của bạn khi phải lâu lâu phải chạy ra xem có ai đó đang đứng ở trước cửa hay không. Giải pháp là bạn lắp chuông, để có khi ai bấm thì chuông đổ (ngắt) bạn biết có người đến trước cửa.

Trở lại với chương trình của chúng ta, chúng ta sẽ sử dụng ngắt để biết khi nào có nhấn nút. Trong tài liệu esp8266-technical_reference_en.pdf có hướng dẫn các bước khai báo xử lý ngắt như sau:


 

1.       Cấu hình chức năng GPIO13 cho Pin Name MTCK_U

PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);

2.       Cấu hình MTCK_U ở chế độ input

GPIO_DIS_OUTPUT(GPIO_ID_PIN(13));

3.       Vô hiệu hóa ngắt trên tất cả các chân trên ESP8266

ETS_GPIO_INTR_DISABLE();

4.       Cài đặt hàm sẽ được gọi khi có ngắt xảy ra

ETS_GPIO_INTR_ATTACH(buttonInteruptHandle, NULL);

5.       Cấu hình MTCK_U là ngắt cạnh xuống vì nhấn nút, tín hiệu sẽ xuống ground (do ta cài đặt internal pullup cho GPIO13).

gpio_pin_intr_state_set(GPIO_ID_PIN(13), GPIO_PIN_INTR_NEGEDGE);

6.       Kích hoạt ngắt GPIO

ETS_GPIO_INTR_ENABLE();

 

             Chúng ta khởi tạo chọn chức năng GPIO và khai báo ngắt trên nút nhấn ở hàm user_init()

void ICACHE_FLASH_ATTR

user_init(void)

{

    uart_init(115200,115200);

    os_printf("\n=======================================\n");

    os_printf("\t\t SDK version: %s \n", system_get_sdk_version());

    os_printf("\n=======================================\n");

 

    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12); //chức năng GPIO12 cho MTDI

    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);//chức năng GPIO13 cho MTCK

    GPIO_OUTPUT_SET(LED_PIN, 0); //set output cho GPIO12

    GPIO_DIS_OUTPUT(BUTTON_PIN); //set input cho GPIO13

    PIN_PULLUP_EN(PERIPHS_IO_MUX_MTCK_U);//set pullup cho GPIO13

 

    ETS_GPIO_INTR_DISABLE(); // hiệu hóa ngắt trên các chân

    ETS_GPIO_INTR_ATTACH(button_interupt_handle, NULL); //hàm được gọi khi ngắt

    gpio_pin_intr_state_set(BUTTON_PIN, GPIO_PIN_INTR_NEGEDGE); //ngắt cạnh xuống

 

    ETS_GPIO_INTR_ENABLE(); //Kích hoạt ngắt

}

 

              Hàm xử lý khi có ngắt ở nút nhấn như sau:

void button_interupt_handle()

{

       ETS_GPIO_INTR_DISABLE(); // hiệu hóa ngắt trên các chân

       //gpio_pin_intr_state_set(BUTTON_PIN, GPIO_PIN_INTR_DISABLE);

       os_delay_us(5000);

 

       uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); //đọc trạng thái ngắt GPIO

       GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status); //xóa cờ ngắt

 

       if (gpio_status & BIT(BUTTON_PIN)){ //Nếu thực sự ngắt xảy ranút nhấn

             if (!GPIO_INPUT_GET(BUTTON_PIN)){

                    led_state = !led_state;

                    GPIO_OUTPUT_SET(LED_PIN, led_state);

                    os_printf("LED state: %d \n", led_state);

             }

       }

 

       gpio_pin_intr_state_set(BUTTON_PIN, GPIO_PIN_INTR_NEGEDGE);//ngắt cạnh xuống

       ETS_GPIO_INTR_ENABLE();// Kích hoạt lại ngắt sau khi đã disable ở trên

}

 

BIÊN DỊCH VÀ NẠP VÀO ESP8266

Copy thư mục HelloWorld, đổi tên thành 2-buttonLED sau đó mở Eclipse import vào (xem hướng dẫn import ở bài trước).


 

Chỉnh sửa file user_main.c trong thư mục 2-buttonLED/app/user có nội dung toàn bộ như sau:

#include "osapi.h"

#include "user_interface.h"

#include "gpio.h"

#include "ets_sys.h"

 

#define LED_PIN 12

#define BUTTON_PIN 13

 

static bool led_state = false;

 

void button_interupt_handle()

{

       ETS_GPIO_INTR_DISABLE(); // hiệu hóa ngắt trên các chân

       //gpio_pin_intr_state_set(BUTTON_PIN, GPIO_PIN_INTR_DISABLE);

       os_delay_us(5000);

 

       uint32 gpioStatus = GPIO_REG_READ(GPIO_STATUS_ADDRESS); //đọc trạng thái ngắt GPIO

       GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpioStatus); //xóa cờ ngắt

 

       if (gpioStatus & BIT(BUTTON_PIN)){ //Nếu thực sự ngắt xảy ranút nhấn

             if (!GPIO_INPUT_GET(BUTTON_PIN)){

                    led_state = !led_state;

                    GPIO_OUTPUT_SET(LED_PIN, led_state);

                    os_printf("LED state: %d \n", led_state);

             }

       }

 

       gpio_pin_intr_state_set(BUTTON_PIN, GPIO_PIN_INTR_NEGEDGE);

       ETS_GPIO_INTR_ENABLE();// Kích hoạt lại ngắt sau khi đã disable ở trên

}

 

 

void ICACHE_FLASH_ATTR

user_init(void)

{

       uart_init(115200,115200);

       os_printf("\n=======================================\n");

    os_printf("\t\t SDK version: %s \n", system_get_sdk_version());

    os_printf("\n=======================================\n");

 

    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12); //chức năng GPIO12 cho MTDI

    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);//chức năng GPIO13 cho MTCK

    GPIO_OUTPUT_SET(LED_PIN, 0); //set output cho GPIO12

    GPIO_DIS_OUTPUT(BUTTON_PIN); //set input cho GPIO13

    PIN_PULLUP_EN(PERIPHS_IO_MUX_MTCK_U);//set pullup cho GPIO13

 

    ETS_GPIO_INTR_DISABLE(); // hiệu hóa ngắt trên các chân

    ETS_GPIO_INTR_ATTACH(button_interupt_handle, NULL); //hàm được gọi khi ngắt

    gpio_pin_intr_state_set(BUTTON_PIN, GPIO_PIN_INTR_NEGEDGE); //ngắt cạnh xuống

 

    ETS_GPIO_INTR_ENABLE(); //Kích hoạt ngắt

}

 

/******************************************************************************

 * FunctionName : user_rf_cal_sector_set

 * Description  : SDK just reversed 4 sectors, used for rf init data and paramters.

 *                We add this function to force users to set rf cal sector, since

 *                we don't know which sector is free in user's application.

 *                sector map for last several sectors : ABCCC

 *                A : rf cal

 *                B : rf init data

 *                C : sdk parameters

 * Parameters   : none

 * Returns      : rf cal sector

*******************************************************************************/

uint32 ICACHE_FLASH_ATTR

user_rf_cal_sector_set(void)

{

    enum flash_size_map size_map = system_get_flash_size_map();

    uint32 rf_cal_sec = 0;

 

    switch (size_map) {

        case FLASH_SIZE_4M_MAP_256_256:

            rf_cal_sec = 128 - 5;

            break;

 

        case FLASH_SIZE_8M_MAP_512_512:

            rf_cal_sec = 256 - 5;

            break;

 

        case FLASH_SIZE_16M_MAP_512_512:

        case FLASH_SIZE_16M_MAP_1024_1024:

            rf_cal_sec = 512 - 5;

            break;

 

        case FLASH_SIZE_32M_MAP_512_512:

        case FLASH_SIZE_32M_MAP_1024_1024:

            rf_cal_sec = 1024 - 5;

            break;

 

        case FLASH_SIZE_64M_MAP_1024_1024:

            rf_cal_sec = 2048 - 5;

            break;

        case FLASH_SIZE_128M_MAP_1024_1024:

            rf_cal_sec = 4096 - 5;

            break;

        default:

            rf_cal_sec = 0;

            break;

    }

 

    return rf_cal_sec;

}

 

void ICACHE_FLASH_ATTR

user_rf_pre_init(void)

{

}

              Biên dịch vào nạp vào ESP8266, xem bài trước có hướng dẫn nạp.

 


Nhận xét

Bài đăng phổ biến từ blog này

Điều khiển đèn bằng router Dlink DIR615C2 thông qua web.

Bài 3 - Blink LED

Mod USB cho Router wifi DLink DIR615 E4