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
Để đọ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();
//Vô 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 có 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();
//Vô 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ự
có ngắt xảy ra ở nú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();
//Vô 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ự có
ngắt xảy ra ở nú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(); //Vô 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 có 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
Đăng nhận xét