Objetivos
Solução possível:
Para implementar uma task síncrona usando FreeRTOS para gerar uma onda quadrada na frequência de 10 KHz e outra task que roda a cada 100 ms mostrando o nível lógico da onda, vamos usar a IDE Eclipse e programar para a placa ESP32. Além disso, vamos usar o botão built-in para pausar e retomar o sinal.
Nota: é fácil gerar uma onda quadrada, simplesmente alternando o nível lógico de certo pino. Neste caso, supondo duty-cycle de 50%, metade do período de tempo estaremos em nível lógico alto ou biaox, o que significa que para gerar a onda quadrada com 10 KHz, metade do período corresponde à: segundos 0,1 mili-segundos 100 s. Metade deste valor corresponde à 50 micro-secundos, ou seja, devemos alternar o sinal na frequência de 20 KHz. Então necessitamos programar a task síncrona para ser interrompida na frequência de 20 KHz. Lembrar que o clock comum de uma placa ESP32 é de 80 MHz ou s s; se for considerado que o ESP32 consegue executar uma insfrução à cada ciclo de máquin ou 4 pulsos de clock, resulta no período de tempo de: 50 ns (ou frequência de 20 MHz).
Certifique-se de ter a ESP-IDF (Espressif IoT Development Framework) instalada e configurada com Eclipse IDE. Este guia assume que você já tem o ambiente configurado.
A seguir segue código completo para IDE Eclipse ou Terminal de comandos:
xxxxxxxxxx
// Definindo o GPIO do LED e do botão
// Pino para gerar a onda quadrada
// Botão built-in
// Divisor do timer (80 MHz / 16 = 5 MHz)
// Frequência do timer
// 50 microsegundos para 10kHz
static xQueueHandle gpio_evt_queue = NULL;
static volatile int is_running = 1; // Flag para controlar o estado do sinal
// ISR do botão
static void IRAM_ATTR gpio_isr_handler(void* arg) {
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
// Task para tratar a interrupção do botão
static void button_task(void* arg) {
uint32_t io_num;
for(;;) {
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
is_running = !is_running; // Alterna o estado do sinal
gpio_set_level(GPIO_OUTPUT_IO, 0); // Garante que o sinal seja zero quando pausado
}
}
}
// Task para gerar a onda quadrada
static void square_wave_task(void* arg) {
// Configuração do timer
timer_config_t config = {
.divider = TIMER_DIVIDER,
.counter_dir = TIMER_COUNT_UP,
.counter_en = TIMER_PAUSE,
.alarm_en = TIMER_ALARM_EN,
.auto_reload = TIMER_AUTORELOAD_EN,
};
timer_init(TIMER_GROUP_0, TIMER_0, &config);
timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL);
timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, TIMER_INTERVAL0_SEC * TIMER_SCALE);
timer_enable_intr(TIMER_GROUP_0, TIMER_0);
timer_start(TIMER_GROUP_0, TIMER_0);
int level = 0;
for(;;) {
if (is_running) {
timer_get_counter_value(TIMER_GROUP_0, TIMER_0, NULL);
timer_pause(TIMER_GROUP_0, TIMER_0);
timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL);
timer_start(TIMER_GROUP_0, TIMER_0);
level = !level;
gpio_set_level(GPIO_OUTPUT_IO, level);
// Aguarda até o próximo alarme
timer_wait_for_intr(TIMER_GROUP_0, TIMER_0);
} else {
vTaskDelay(pdMS_TO_TICKS(10)); // Aguarda um pouco quando pausado
}
}
}
// Task para monitorar o estado do sinal
static void monitor_task(void* arg) {
for(;;) {
int level = gpio_get_level(GPIO_OUTPUT_IO);
printf("Nível lógico da onda: %d\n", level);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void app_main() {
// Configura os pinos GPIO
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_NEGEDGE; // Interrupção na borda de descida
io_conf.pin_bit_mask = (1ULL << GPIO_INPUT_IO);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = (1ULL << GPIO_OUTPUT_IO);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
// Cria a fila de eventos GPIO
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_isr_handler_add(GPIO_INPUT_IO, gpio_isr_handler, (void*) GPIO_INPUT_IO);
// Cria as tasks
xTaskCreate(button_task, "button_task", 2048, NULL, 10, NULL);
xTaskCreate(square_wave_task, "square_wave_task", 2048, NULL, 10, NULL);
xTaskCreate(monitor_task, "monitor_task", 2048, NULL, 10, NULL);
}
Configuração do GPIO:
GPIO_OUTPUT_IO
é configurado como saída para gerar a onda quadrada.GPIO_INPUT_IO
é configurado como entrada para o botão built-in com uma interrupção configurada na borda de descida.Task button_task
:
is_running
.Task square_wave_task
:
Task monitor_task
:
timer_wait_for_intr
que espera a próxima interrupção do timer. Certifique-se de adaptar a função de acordo com a API de timers do ESP-IDF.printf
no monitor serial.is_running
controla este comportamento.monitor_task
.Compile e carregue este código na sua ESP32 usando a IDE Eclipse configurada para ESP-IDF. A task button_task
controlará o estado do sinal e a task monitor_task
exibirá o nível lógico da onda.
Modificação para ativar o builtin led para indicar se onda quadrada está sendo gerada ou não:
Para modificar a task button_task
para ativar o LED built-in da placa ESP32 quando a onda quadrada estiver sendo gerada, podemos fazer o seguinte:
button_task
para alternar o estado do LED conforme o estado da flag is_running
.A seguir segue código para Eclipse (ou terminal de comandos):
xxxxxxxxxx
// Definindo o GPIO do LED, do LED built-in e do botão
// Pino para gerar a onda quadrada
// Botão built-in
// LED built-in
// Divisor do timer (80 MHz / 16 = 5 MHz)
// Frequência do timer
// 50 microsegundos para 10kHz
static xQueueHandle gpio_evt_queue = NULL;
static volatile int is_running = 1; // Flag para controlar o estado do sinal
// ISR do botão
static void IRAM_ATTR gpio_isr_handler(void* arg) {
uint32_t gpio_num = (uint32_t) arg;
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
// Task para tratar a interrupção do botão
static void button_task(void* arg) {
uint32_t io_num;
for(;;) {
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
is_running = !is_running; // Alterna o estado do sinal
gpio_set_level(GPIO_OUTPUT_IO, 0); // Garante que o sinal seja zero quando pausado
gpio_set_level(GPIO_BUILTIN_LED, is_running); // Define o estado do LED built-in
}
}
}
// Task para gerar a onda quadrada
static void square_wave_task(void* arg) {
// Configuração do timer
timer_config_t config = {
.divider = TIMER_DIVIDER,
.counter_dir = TIMER_COUNT_UP,
.counter_en = TIMER_PAUSE,
.alarm_en = TIMER_ALARM_EN,
.auto_reload = TIMER_AUTORELOAD_EN,
};
timer_init(TIMER_GROUP_0, TIMER_0, &config);
timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL);
timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, TIMER_INTERVAL0_SEC * TIMER_SCALE);
timer_enable_intr(TIMER_GROUP_0, TIMER_0);
timer_start(TIMER_GROUP_0, TIMER_0);
int level = 0;
for(;;) {
if (is_running) {
timer_get_counter_value(TIMER_GROUP_0, TIMER_0, NULL);
timer_pause(TIMER_GROUP_0, TIMER_0);
timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL);
timer_start(TIMER_GROUP_0, TIMER_0);
level = !level;
gpio_set_level(GPIO_OUTPUT_IO, level);
// Aguarda até o próximo alarme
timer_wait_for_intr(TIMER_GROUP_0, TIMER_0);
} else {
vTaskDelay(pdMS_TO_TICKS(10)); // Aguarda um pouco quando pausado
}
}
}
// Task para monitorar o estado do sinal
static void monitor_task(void* arg) {
for(;;) {
int level = gpio_get_level(GPIO_OUTPUT_IO);
printf("Nível lógico da onda: %d\n", level);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void app_main() {
// Configura os pinos GPIO
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_NEGEDGE; // Interrupção na borda de descida
io_conf.pin_bit_mask = (1ULL << GPIO_INPUT_IO);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = (1ULL << GPIO_OUTPUT_IO);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
// Configura o LED built-in como saída
io_conf.pin_bit_mask = (1ULL << GPIO_BUILTIN_LED);
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
// Cria a fila de eventos GPIO
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_isr_handler_add(GPIO_INPUT_IO, gpio_isr_handler, (void*) GPIO_INPUT_IO);
// Cria as tasks
xTaskCreate(button_task, "button_task", 2048, NULL, 10, NULL);
xTaskCreate(square_wave_task, "square_wave_task", 2048, NULL, 10, NULL);
xTaskCreate(monitor_task, "monitor_task", 2048, NULL, 10, NULL);
}
Definição do LED Built-in:
#define GPIO_BUILTIN_LED 2
: Adicionada definição do pino do LED built-in.Configuração do LED Built-in:
GPIO_BUILTIN_LED
como saída no app_main
.Modificação na button_task
:
gpio_set_level(GPIO_BUILTIN_LED, is_running)
para definir o estado do LED built-in conforme o estado de is_running
.Agora, quando a onda quadrada estiver sendo gerada (is_running
for verdadeiro), o LED built-in estará aceso. Quando a geração da onda estiver pausada (is_running
for falso), o LED built-in estará apagado.
Código anterior levemente alterado para interface IDE do Arduino:
Claro! A seguir, vou adaptar o código anterior para ser usado na interface IDE do Arduino com a placa ESP32. O código irá gerar uma onda quadrada com frequência de 10 kHz, monitorar o botão built-in para pausar ou retomar a geração da onda e ativar o LED built-in quando a onda estiver sendo gerada.
xxxxxxxxxx
// Definindo o GPIO do LED, do LED built-in e do botão
// Pino para gerar a onda quadrada (LED built-in)
// Botão built-in
volatile int is_running = 1; // Flag para controlar o estado do sinal
volatile int level = 0; // Nível lógico da onda
hw_timer_t *timer = NULL; // Ponteiro para o timer
// ISR do botão
void IRAM_ATTR button_isr() {
is_running = !is_running; // Alterna o estado do sinal
digitalWrite(GPIO_OUTPUT_IO, LOW); // Garante que o sinal seja zero quando pausado
digitalWrite(LED_BUILTIN, is_running); // Define o estado do LED built-in
}
// ISR do timer
void IRAM_ATTR onTimer() {
if (is_running) {
level = !level;
digitalWrite(GPIO_OUTPUT_IO, level);
}
}
void setup() {
// Configura os pinos GPIO
pinMode(GPIO_OUTPUT_IO, OUTPUT); // Configura o pino de saída
pinMode(GPIO_INPUT_IO, INPUT_PULLUP); // Configura o botão com pull-up interno
pinMode(LED_BUILTIN, OUTPUT); // Configura o LED built-in como saída
// Configura a interrupção do botão
attachInterrupt(digitalPinToInterrupt(GPIO_INPUT_IO), button_isr, FALLING);
// Configura o timer
timer = timerBegin(0, 80, true); // Timer 0, prescaler de 80 para 1 MHz
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 50, true); // 50 microsegundos para 10 kHz
timerAlarmEnable(timer); // Ativa o alarme do timer
// Inicializa o serial para monitorar o nível lógico da onda
Serial.begin(115200);
}
void loop() {
static unsigned long lastMillis = 0;
// Monitoramento do estado do sinal a cada 100 ms
if (millis() - lastMillis >= 100) {
lastMillis = millis();
int current_level = digitalRead(GPIO_OUTPUT_IO);
Serial.printf("Nível lógico da onda: %d\n", current_level);
}
}
Configuração dos Pinos GPIO:
GPIO_OUTPUT_IO
é configurado como saída para gerar a onda quadrada.GPIO_INPUT_IO
é configurado como entrada para o botão built-in com pull-up interno.LED_BUILTIN
(pino 2) é configurado como saída para indicar o estado da geração da onda.Interrupção do Botão:
button_isr
alterna a flag is_running
, define o sinal para baixo (LOW) e ajusta o estado do LED built-in.Interrupção do Timer:
onTimer
alterna o nível lógico do pino de saída a cada interrupção do timer se is_running
estiver verdadeiro.Setup:
Loop Principal:
Essa adaptação deve funcionar de maneira semelhante ao código original, agora dentro da interface do Arduino, tornando o desenvolvimento e a depuração mais acessíveis para quem está acostumado com esse ambiente.
Atenção: Falta testar estas rotinas. Parece que usam versões atrasadas das API's da Espressif.
🌊 Fernando Passold 📬 ,