Was soll hier gezeigt werden?
Für mich war die ESP-IDF neu, darum habe ich folgendes ausprobiert, um festzustellen, wie elegant sich das lösen lässt. Folgende Punkte sollen gezeigt werden:
- Aufbau mit mehreren Dateien
- Externe Variable
- Interrupt-Routine
- Tasks auf unterschiedlichen CPU- Kernen
- Semaphore
- I/O Benutzung
Als Hardware habe ich mein Eval-Board benutzt.
Für mich war die ESP-IDF neu, darum habe ich folgendes ausprobiert, um festzustellen, wie elegant sich das lösen lässt. Folgende Punkte sollen gezeigt werden:
- Aufbau mit mehreren Dateien
- Externe Variable
- Interrupt-Routine
- Tasks auf unterschiedlichen CPU- Kernen
- Semaphore
- I/O Benutzung
Als Hardware habe ich mein Eval-Board benutzt.
void app_main(void)
Die Funktion „app_main(void)“ wird nach dem Reset aufgerufen, sie ist vergleichbar mit der „setup()“ Funktion bei arduino. Somit werden hier zuerst die Grundsettings vorgenommen.
Aber es gibt keine „loop()“ Funktion, sondern es wird eine „while(1){}“ Schleife in „app_main()“ installiert, die die „loop()“ Funktion ersetzt.
#include
#include
#include
#include
#include
#include
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_task_wdt.h"
#include "task_2.h"
#include "freertos/semphr.h"
#define TWDT_TIMEOUT_MS 10000
#define TASK_RESET_PERIOD_MS 1500
#define MAIN_DELAY_MS 10000
#define BUTTON_GPIO GPIO_NUM_4 // Pushbutton GPIO
#define YELLOW_LED GPIO_NUM_13 // LED GPIO
#define RED_LED GPIO_NUM_14 // LED GPIO
#define GREEN_LED GPIO_NUM_12 // LED GPIO
#define DEBOUNCE_DELAY_US 200000ULL // Debounce delay in microseconds (200 ms)
static volatile uint64_t last_isr_time = 0;
static volatile uint32_t counter = 0;
static QueueHandle_t button_queue;
int button_counter=0;
extern int ext_data;
SemaphoreHandle_t xGlobalMutex=NULL;
// Task 1
void task_func(void *arg)
{
while (1) {
printf("Task1 is running and LED is on.\n");
gpio_set_level(GREEN_LED, 0); // Turn on green LED to indicate task is running
vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_MS));
printf("Task1 is running and LED is off.\n");
gpio_set_level(GREEN_LED, 1); // Turn off green LED to indicate task is running
vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_MS));
set_counter(button_counter++);
if (xSemaphoreTake(xGlobalMutex, portMAX_DELAY)) {
ext_data++;
xSemaphoreGive(xGlobalMutex);
}
printf("Number Task 2 %d\n", reverse_count());
}
}
// Interrupt Service Routine (ISR) for button press, placed in IRAM for low latency
static void IRAM_ATTR button_isr(void *arg) {
uint64_t now = esp_timer_get_time(); // Get current time in microseconds
// Check if debounce period has passed, then process the button press
if (now - last_isr_time > DEBOUNCE_DELAY_US) {
counter++;
uint32_t cnt = counter;
BaseType_t higher_priority_task_woken = pdFALSE;
xQueueSendFromISR(button_queue, &cnt, &higher_priority_task_woken); // Send counter to queue from ISR
last_isr_time = now;
if (higher_priority_task_woken) {
portYIELD_FROM_ISR();
}
}
}
void app_main(void) {
// Init Hardware and data
xGlobalMutex = xSemaphoreCreateMutex();
printf("Press the button on GPIO %d.\n", BUTTON_GPIO);
gpio_reset_pin(YELLOW_LED);
gpio_reset_pin(RED_LED);
gpio_reset_pin(GREEN_LED);
gpio_set_direction(YELLOW_LED, GPIO_MODE_OUTPUT);
gpio_set_direction(RED_LED, GPIO_MODE_OUTPUT);
gpio_set_direction(GREEN_LED, GPIO_MODE_OUTPUT);
// Create a queue to hold up to 10 uint32_t items
button_queue = xQueueCreate(10, sizeof(uint32_t));
// Configure Button GPIO
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_POSEDGE, // Rising edge interrupt trigger
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = (1ULL << BUTTON_GPIO),
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pull_up_en = GPIO_PULLUP_ENABLE
};
gpio_config(&io_conf);
// Install GPIO ISR service
gpio_install_isr_service(0);
// Add ISR handler for button
gpio_isr_handler_add(BUTTON_GPIO, button_isr, NULL);
// Variable to receive counter from queue
uint32_t button_counter;
gpio_set_level(GREEN_LED, 0); // Turn on green LED to indicate program is running
gpio_set_level(YELLOW_LED, 1); // Turn off yellow LED to indicate program is running
gpio_set_level(RED_LED, 1); // Turn off red LED to indicate program is running
printf("Task creation.\n");
//Init tasks
xTaskCreatePinnedToCore(task_func, "task", 2048, xTaskGetCurrentTaskHandle(), 10, NULL, 0);
init_task_2();
// Keep program running
//loop() function
while (1) {
// Wait indefinitely for an item in the queue
//gpio_set_level(YELLOW_LED, 1); // Turn on yellow LED to indicate waiting for button press
if (xQueueReceive(button_queue, &button_counter, portMAX_DELAY)) {
//gpio_set_level(YELLOW_LED, 0); // Turn off yellow LED
gpio_set_level(RED_LED, 0); // Turn on red LED to indicate button press
printf("Button pressed %lu times.\n", button_counter);
vTaskDelay(200 / portTICK_PERIOD_MS); // Delay to keep red LED on for 200ms
gpio_set_level(RED_LED, 1); // Turn off red LED
}
}
}
Wie funktioniert das mit dem Interrupt?
Die Interrupt-Routine überwacht einen Taster, der beim Auslösen eine „xQueueSendFromISR auslöst und diesen an die Loop-Funtion in „app_main()“ sendet, die mit „xQueueReceive()“ darusf wartet, wass dann die rote LED für 200Ms leuchten läßt.
Was macht Task 1?
Task 1 lässt die grüne LED für 1,5 Sek leuchten. Danach wird die externe Funktion „set_counter()“ aufgerufen, die den aktuellen „button_counter“ sendet.
Dann wird unter Mutex-Sperre „semaphoreTake()“ und semaphoreGive()“ die externe Variable erhöht.
// File: data.c
int ext_data = 0;
// File: task_2.h
void init_task_2(void) ;
void set_counter (int cnt);
// File: task_2.c
#include
#include
#include
#include
#include
#include
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_task_wdt.h"
#include "task_2.h"
#include "freertos/semphr.h"
#define TWDT_TIMEOUT_MS 10000
#define TASK_RESET_PERIOD_MS1 1000
#define MAIN_DELAY_MS 10000
#define YELLOW_LED GPIO_NUM_13 // LED GPIO
#define RED_LED GPIO_NUM_14 // LED GPIO
#define GREEN_LED GPIO_NUM_12 // LED GPIO
#define DEBOUNCE_DELAY_US 200000ULL // Debounce delay in microseconds (200 ms)
int button_counter1=0;
extern int ext_data;
extern SemaphoreHandle_t xGlobalMutex;
void set_counter(int cnt) {
button_counter1 = cnt;
printf("Counter updated to %d\n", button_counter1);
}
void task_func_2(void *arg)
{
while (1) {
printf("Task2 is running and LED is on.\n");
gpio_set_level(YELLOW_LED, 0); // Turn on yellow LED to indicate task is running
vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_MS1));
printf("Task2 is running and LED is off.\n");
gpio_set_level(YELLOW_LED, 1); // Turn off yellow LED to indicate task is running
vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_MS1));
printf("Task2: BUTTON_COUNTER1 = %d\n", button_counter1);
if (xSemaphoreTake(xGlobalMutex, portMAX_DELAY)) {
printf("Task2: external_data = %d\n",ext_data);
xSemaphoreGive(xGlobalMutex);
}
}
}
void init_task_2(void) {
xTaskCreatePinnedToCore(task_func_2, "task_2", 2048, NULL, 10, NULL, 1);
}
Was macht Task2?
Task2 macht periodisch die gelbe LED an und aus.
Auch liest sie in jedem Zyklus der Schleife die externe Variable „ext_data“ aus , die in der Task 1 hochgezählt wird. Natürlich durch Mutex geschützt.
„init_task_2()“ wird zum Starten der Task 2 aus „app_main()“ aufgerufen.
//CMAKELists.txt
idf_component_register(SRCS "data.c" "main.c" "task_2.c" "data.c"
INCLUDE_DIRS ".")
Zum Schluss zeige ich noch den Makefile für das Projekt.
Als Basis diente der RNT Kurs „ESP-IDF: ESP32 GPIO-Interrupts (Taster mit Entprellung)„
