Kỹ thuật điện tử & Điện lạnh

Lập trình ESP-IDF cảm biến chạm sử dụng FreeRtos kiến thức mới năm 2023

Lập trình ESP-IDF cảm biến chạm sử dụng FreeRtos – Cập nhật kiến thức mới nhất năm 2023

Như chúng ta đã biết, FreeRTOS là hệ điều hành thời gian thực chính thức được hỗ trợ bởi ESP32. FreeRTOS ban đầu được thiết kế cho các kiến ​​trúc lõi đơn. Tuy nhiên, ESP32 có hai lõi, và do đó, cổng FreeRTOS này cũng được sửa đổi để xử lý các hệ thống 2 lõi. Hầu hết sự khác biệt giữa FreeRTOS và ESP-IDF FreeRTOS bắt nguồn từ lý do này. Đối với những người đã có một số kinh nghiệm với FreeRTOS, chỉ cần lướt qua là đủ những khác biệt này:
• Tạo một nhiệm vụ mới: Chúng ta có một chức năng mới, nơi chúng ta có thể chỉ định lõi nào để chạy một tác vụ mới; nó là xTaskCreatePinnedToCore. Hàm này nhận một tham số để đặt mối quan hệ của nhiệm vụ với lõi được chỉ định. Nếu một tác vụ được tạo bởi xTaskCreate ban đầu, nó không thuộc về bất kỳ lõi nào và bất kỳ lõi nào cũng có thể chọn chạy nó ở lần ngắt đánh dấu tiếp theo.
• Đình chỉ bộ lập lịch: Lệnh gọi hàm vTaskSuspendAll chỉ tạm dừng bộ lập lịch trên lõi mà nó được gọi. Các lõi còn lại tiếp tục hoạt động của nó. Do đó, việc tạm ngưng bộ lập lịch để bảo vệ tài nguyên được chia sẻ không phải là cách đúng.
• Phần quan trọng: Việc nhập phần quan trọng sẽ dừng bộ lập lịch và chỉ ngắt trên lõi gọi. Các lõi còn lại tiếp tục hoạt động của nó. Tuy nhiên, phần quan trọng vẫn được bảo vệ bằng mutex, ngăn lõi khác chạy phần quan trọng cho đến khi lõi đầu tiên thoát ra. Chúng tôi có thể sử dụng macro portENTER_CRITICAL_SAFE (mux) và portEXIT_CRITICAL_SAFE (mux) cho mục đích này.

Lưu ý quan trọng
ESP32 lõi kép đặt tên cho các lõi của nó là PRO_CPU (cpu0) và APP_CPU (cpu1). PRO_CPU bắt đầu khi ESP32 được cấp nguồn lần đầu tiên và thực hiện tất cả quá trình khởi tạo, bao gồm cả kích hoạt APP_CPU. app_main được gọi từ tác vụ chính chạy trên PRO_CPU.

Nếu bạn chưa quen với FreeRTOS, có một số tài liệu tuyệt vời có sẵn trên trang web của nó tại đây: Espressif cũng giải thích chi tiết những gì khác biệt trong phiên bản ESP-IDF của FreeRTOS tại đây: Hãy xem một ví dụ về cách bảo vệ tài nguyên được chia sẻ trong FreeRTOS ở các ví dụ sắp tới đây.

Chúng ta chỉ có thể chuyển sang mã nguồn để thảo luận về cách chúng ta triển khai ứng dụng này:

 

 

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/touch_pad.h"
#include <string.h>
static portMUX_TYPE mut = portMUX_INITIALIZER_UNLOCKED;
typedef struct
{
int pin_num;
TickType_t when;
} touch_info_t;
#define TI_LIST_SIZE 10
static volatile touch_info_t ti_list[TI_LIST_SIZE];
static volatile size_t ti_cnt = 0;
static const TickType_t check_period = 500 / portTICK_PERIOD_
MS;

driver / touch_pad.h chứa các hàm và định nghĩa loại để sử dụng các thiết bị ngoại vi cảm ứng. Chúng ta sử dụng mutex, mut, thuộc loại portMUX_TYPE, để bảo vệ các tài nguyên được chia sẻ khỏi truy cập đồng thời. Định nghĩa kiểu mutex dành riêng cho ESP-IDF. Các tài nguyên được chia sẻ của chúng ta là ti_listti_cnt, cần được bảo vệ bởi mut. Tiếp theo, ta triển khai ISR xử lý touch pad:

 

static void IRAM_ATTR tp_handler(void *arg)
{
uint32_t pad_intr = touch_pad_get_status();
touch_pad_clear_status();
touch_info_t touch = {
.pin_num = (pad_intr >> TOUCH_PAD_NUM8) & 0x01 ? TOUCH_
PAD_NUM8 : TOUCH_PAD_NUM9,
.when = xTaskGetTickCountFromISR()};

tp_handler là ISR cho thiết bị ngoại vi cảm ứng. Nếu bất kỳ kênh nào trong hai kênh phát hiện có chạm, ISR này sẽ chạy. Trong tp_handler, chúng ta đọc từ thiết bị ngoại vi cảm ứng (được gọi là touch_pad) bằng cách gọi touch_pad_get_status để hiểu chân nào được chạm và lưu thông tin này vào biến số của cảm ứng, cùng với thời gian tính bằng tích tắc. Chúng ta sử dụng xTaskGetTickCountFromISR của FreeRTOS cho việc này.

 

portENTER_CRITICAL_SAFE(&mut);
if (ti_cnt < TI_LIST_SIZE)
{
bool skip = (ti_cnt > 0) &&
((touch.when - ti_list[ti_cnt - 1].when) < check_period) &&
(touch.pin_num == ti_list[ti_cnt - 1].pin_num);
if (!skip)
{
ti_list[ti_cnt++] = touch;
}
}
portEXIT_CRITICAL_SAFE(&mut);
}

Sau đó, chúng ta sẽ phải nhập phần quan trọng bằng cách gọi macro portENTER_CRITICAL_SAFE với địa chỉ mutex. Phần này rất quan trọng vì chúng ta sẽ sửa đổi các giá trị ti_listti_cnt và chúng phải nhất quán. Để đạt được tính nhất quán, chúng ta phải ngăn chặn bất kỳ quyền truy cập nào vào các biến đó trong khi sửa đổi nó trong ISR. Sau khi hoàn tất cập nhật, gọi portEXIT_CRITICAL_SAFE để giải phóng mutex và do đó, các tài nguyên được chia sẻ có thể truy cập miễn phí từ bất kỳ tác vụ nào khác. Trong hàm tiếp theo, chúng tôi sẽ in thông tin lên màn hình monitor :

 

static void monitor(void *arg)
{
touch_info_t ti_list_local[TI_LIST_SIZE];
size_t ti_cnt_local;
while (1)
{
vTaskDelay(10000 / portTICK_PERIOD_MS);
ti_cnt_local = 0;
portENTER_CRITICAL_SAFE(&mut);
if (ti_cnt > 0)
{
memcpy((void *)ti_list_local, (const void *)ti_list, ti_cnt * sizeof(touch_info_t));
ti_cnt_local = ti_cnt;
ti_cnt = 0;
}
portEXIT_CRITICAL_SAFE(&mut);

Chúng ta sẽ in thông tin liên lạc đã thu thập cứ sau 10 giây. Trong vòng lặp while, chúng ta một lần nữa lại sử dụng cặp portENTER_CRITICAL_SAFEportEXIT_CRITICAL_SAFE để truy cập các giá trị ti_listti_cnt chung bằng cách che chắn chúng bằng mutex. Điểm quan trọng ở đây là sao chép các giá trị của các tài nguyên được chia sẻ đó vào các biến cục bộ, ti_list_localti_cnt_local, để giữ cho phần quan trọng càng ngắn càng tốt để cung cấp các tài nguyên được chia sẻ cho trình xử lý ngắt trong thời gian ngắn nhất. Trong phần còn lại của hàm, chúng ta chỉ cần in các thống kê mà chúng ta đã sao chép vào biến cục bộ:

if (ti_cnt_local > 0)
{
int t8_cnt = 0;
for (int i = 0; i < ti_cnt_local; ++i)
{
if (ti_list_local[i].pin_num == TOUCH_PAD_NUM8)
{
++t8_cnt;
}
}
printf("First touch tick: %un", ti_list_local[0].
when);
printf("Last touch tick: %un", ti_list_local[ti_
cnt_local - 1].when);
printf("Touch8 count: %dn", t8_cnt);
printf("Touch9 count: %dn", ti_cnt_local - t8_
cnt);
}
else
{
printf("No touch detectedn");
}
}
}

Nếu cảm biến phát hiện bất kỳ lần chạm nào, chúng ta sẽ cho sẽ in dấu phát hiện đầu tiên, dấu phát hiện cuối cùng và số lượng trên mỗi pin. Nếu không có cảm ứng, chúng ta cũng in thông tin này trên màn hình monitor.
Hãy khởi tạo phần cứng tiếp theo:

static void init_hw(void)
{
touch_pad_init();
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5,
TOUCH_HVOLT_ATTEN_1V);
touch_pad_config(TOUCH_PAD_NUM8, 0);
touch_pad_config(TOUCH_PAD_NUM9, 0);
touch_pad_filter_start(10);

Trong hàm init_hw, chúng ta chỉ khởi tạo thiết bị ngoại vi cảm ứng. Đầu tiênkhởi tạo trình điều khiển, sau đó là chế độ máy trạng thái hữu hạn của thiết bị ngoại vi và các giá trị tham chiếu điện áp. Việc tiếp theo cần làm là hiệu chỉnh các chân cảm ứng 8 và 9 với các giá trị ngưỡng làm bộ kích hoạt ngắt. Trong quá trình hiệu chuẩn, phải đảm bảo rằng không có tiếp xúc với các chân này. Chúng tôi cần đọc từ mỗi touch pad và đặt giá trị ngưỡng cho phù hợp. Đoạn mã tiếp theo cho biết cách triển khai hiệu chuẩn:

 

uint16_t val;
touch_pad_read_filtered(TOUCH_PAD_NUM8, &val);
touch_pad_set_thresh(TOUCH_PAD_NUM8, val * 0.2);
touch_pad_read_filtered(TOUCH_PAD_NUM9, &val);
touch_pad_set_thresh(TOUCH_PAD_NUM9, val * 0.2);
touch_pad_isr_register(tp_handler, NULL);
}

Cuối cùng, chúng ta đặt tp_handler làm ISR của ngắt cảm ứng và hoàn tất quá trình khởi tạo phần cứng.

Bây giờ chúng ta có thể tiếp tục với điểm nhập ứng dụng, app_main:

void app_main(void)
{
init_hw();
TaskHandle_t taskh;
if (xTaskCreatePinnedToCore(monitor,
"monitor",
1024,
NULL,
2,
&taskh,
APP_CPU_NUM) == pdPASS)
{
printf("info: monitor startedn");
}
else
{
printf("err: monitor task couldn't startn");
}
char buffer[128];
vTaskList(buffer);
printf("%sn", buffer);
touch_pad_intr_enable();
}

Sau khi gọi init_hw để khởi tạo thiết bị ngoại vi cảm ứng, chúng ta tạo tác vụ màn hình với sự trợ giúp của xTaskCreatePinnedToCore. Nó có bảy tham số, lần lượt như sau:
•  Function chạy như một tác vụ. Đây là monitor function.
.• Name : Tên của nhiệm vụ cho mục đích chẩn đoán.
stack size : Kích thước ngăn xếp để dự trữ cho nhiệm vụ tính bằng byte. Điều này khác với FreeRTOS thuần vì nó nhận tham số này bằng chữ. Nếu chúng ta không đặt giá trị này lớn hơn số lượng tác vụ cần, ứng dụng sẽ gặp sự cố.
• Tham số void * để truyền cho hàm tác vụ. màn hình không cần bất kỳ, vì vậy NULL được thông qua.
The priority : Mức độ ưu tiên của nhiệm vụ. Giá trị thấp có nghĩa là mức độ ưu tiên thấp.
The address of the task handle. Nếu được cung cấp, các hàm xTaskCreate * sẽ đặt giá trị của nó và chúng ta có thể sử dụng task handle để quản lý tác vụ, chẳng hạn như tạm dừng, tiếp tục hoặc xóa.
The Core để chạy nhiệm vụ. Tham số này dành riêng cho xTaskCreatePinnedToCore, chỉ tồn tại trong ESP-IDF FreeRTOS. Chúng ta cung cấp APP_CPU_NUM làm cốt lõi.
Chúng ta có thể xem các tác vụ hiện có trong ứng dụng của mình bằng cách gọi vTaskList với một bộ đệm làm tham số. Nhiệm vụ màn hình của chúng ta cũng được liệt kê trong đầu ra của nó. Điều cuối cùng trong app_main là bật tính năng ngắt cảm ứng.

Trước khi biên dịch mã, chúng ta cũng cần chỉnh sửa platformio.ini, thêm một số định nghĩa để kích hoạt vTaskList như sau:

 

monitor_speed = 115200
build_flags =
-DCONFIG_FREERTOS_USE_TRACE_FACILITY=1
-DCONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=1

Bây giờ chúng ta đã sẵn sàng để biên dịch và chạy chương trình. Để kiểm tra ứng dụng, chúng ta chỉ cần chạm vào các dây được nối với các chân cảm ứng. Chúng ta có thể thấy rằng tác vụ màn hình in số liệu thống kê về cảm ứng trên màn hình nối tiếp cứ sau 10 giây. Trong ví dụ tiếp theo, chúng ta sẽ xem xét cách sử dụng Queue FreeRTOS.

Chúc các bạn thành công !!!

Series Navigation

<< Lập trình ESP-IDF giao tiếp với màn hình LCD1602 hiện thị nhiệt độ đọc từ DHT11Giao tiếp UART với ESP-IDF >>

Kết thúc
Ngoài các bài viết tin tức, bài báo hàng ngày của https://www.kythuatcodienlanh.com/, nguồn nội dung cũng bao gồm các bài viết từ các cộng tác viên chuyên gia đầu ngành về chuỗi kiến thức kỹ thuật điện, điện lạnh, điện tử, cơ khí,…,.. được chia sẽ chủ yếu từ nhiều khía cạnh liên quan chuỗi kiến thức này.
Bạn có thể dành thời gian để xem thêm các chuyên mục nội dung chính với các bài viết tư vấn, chia sẻ mới nhất, các tin tức gần đây từ chuyên gia và đối tác của Chúng tôi. Cuối cùng, với các kiến thức chia sẻ của bài viết, hy vọng góp phần nào kiến thức hỗ trợ cho độc giả tốt hơn trong hoạt động nghề nghiệp cá nhân!
* Ý kiến được trình bày trong bài viết này là của tác giả khách mời và không nhất thiết phải là SEMTEK. Nhân viên tác giả, cộng tác viên biên tập sẽ được liệt kê bên cuối bài viết.
Trân trọng,
Các chuyên mục nội dung liên quan

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Back to top button