Initial commit - sync AliceRC to Gitea
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRCS "main.c" "app_main.c" "adc.c" "battery.c" "led.c" "ui_task.c" "rf_task.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES nrf24l01 oled audio protocol ui driver nvs_flash
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @file adc.c
|
||||
* @brief ADC reading task for joystick channels and battery voltage
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/adc.h"
|
||||
#include "app_main.h"
|
||||
|
||||
static const char *TAG = "adc";
|
||||
|
||||
/* ADC channel to GPIO mapping for ESP32S3 */
|
||||
static const adc2_channel_t adc_channels[] = {
|
||||
ADC2_CHANNEL_0, /* GPIO 4 - CH1 */
|
||||
ADC2_CHANNEL_1, /* GPIO 5 - CH2 */
|
||||
ADC2_CHANNEL_2, /* GPIO 6 - CH3 */
|
||||
ADC2_CHANNEL_3, /* GPIO 7 - CH4 */
|
||||
ADC2_CHANNEL_4, /* GPIO 15 - CH5 */
|
||||
ADC2_CHANNEL_5, /* GPIO 16 - CH6 */
|
||||
ADC2_CHANNEL_6, /* GPIO 17 - CH7 */
|
||||
ADC2_CHANNEL_7, /* GPIO 9 - CH8 */
|
||||
};
|
||||
|
||||
void adc_task(void *pvParameters)
|
||||
{
|
||||
adc_data_t adc_data;
|
||||
int adc_raw = 0;
|
||||
|
||||
ESP_LOGI(TAG, "ADC task started");
|
||||
|
||||
while (1) {
|
||||
/* Read all 8 joystick channels */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
adc2_get_raw(adc_channels[i], ADC_WIDTH_BIT_12, &adc_raw);
|
||||
#else
|
||||
/* ESP32 uses ADC1 for these pins */
|
||||
adc_raw = adc1_get_raw(ADC1_CHANNEL_0 + i);
|
||||
#endif
|
||||
adc_data.channels[i] = (uint16_t)adc_raw;
|
||||
}
|
||||
|
||||
/* Read battery voltage (ADC1_CHANNEL_0 = GPIO 8) */
|
||||
int bat_raw = adc1_get_raw(ADC1_CHANNEL_0);
|
||||
/* Convert ADC reading to mV: 12-bit, 3.3V reference, voltage divider */
|
||||
uint32_t bat_mv = (bat_raw * 3300 * 2) / 4096; /* Simple conversion */
|
||||
adc_data.battery_mv = (uint16_t)bat_mv;
|
||||
|
||||
/* Send to queue (non-blocking) */
|
||||
if (adc_data_queue) {
|
||||
xQueueOverwrite(adc_data_queue, &adc_data);
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); /* 50Hz sample rate */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @file app_main.c
|
||||
* @brief AliceRC application initialization and task management
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/adc.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "app_main.h"
|
||||
|
||||
/* Queue handles for inter-task communication */
|
||||
QueueHandle_t adc_data_queue = NULL;
|
||||
QueueHandle_t rf_command_queue = NULL;
|
||||
QueueHandle_t ui_event_queue = NULL;
|
||||
|
||||
static const char *TAG = "app_main";
|
||||
|
||||
void app_init_gpio(void)
|
||||
{
|
||||
/* Configure button GPIOs as inputs with pull-ups */
|
||||
gpio_config_t btn_conf = {
|
||||
.pin_bit_mask = (1ULL<<BTN_UP_GPIO) | (1ULL<<BTN_DOWN_GPIO) |
|
||||
(1ULL<<BTN_SEL_GPIO) | (1ULL<<BTN_BACK_GPIO),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE
|
||||
};
|
||||
gpio_config(&btn_conf);
|
||||
|
||||
/* Configure status LED */
|
||||
gpio_config_t led_conf = {
|
||||
.pin_bit_mask = (1ULL << STATUS_LED_GPIO),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE
|
||||
};
|
||||
gpio_config(&led_conf);
|
||||
gpio_set_level(STATUS_LED_GPIO, 0);
|
||||
|
||||
/* Configure PWR_EN (ESP32S3 only) */
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
gpio_config_t pwr_conf = {
|
||||
.pin_bit_mask = (1ULL << PWR_EN_GPIO),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE
|
||||
};
|
||||
gpio_config(&pwr_conf);
|
||||
gpio_set_level(PWR_EN_GPIO, 1); /* Enable peripheral power */
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "GPIO initialized");
|
||||
}
|
||||
|
||||
void app_init_adc(void)
|
||||
{
|
||||
/* Initialize ADC1 for battery voltage */
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_12); /* GPIO 8 */
|
||||
|
||||
/* Initialize ADC2 for joystick channels (ESP32S3) */
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
/* ADC2 channels: 4,5,6,7,15,16,17,9 */
|
||||
adc2_config_channel_atten(ADC2_CHANNEL_0, ADC_ATTEN_DB_12); /* GPIO 4 */
|
||||
adc2_config_channel_atten(ADC2_CHANNEL_1, ADC_ATTEN_DB_12); /* GPIO 5 */
|
||||
adc2_config_channel_atten(ADC2_CHANNEL_2, ADC_ATTEN_DB_12); /* GPIO 6 */
|
||||
adc2_config_channel_atten(ADC2_CHANNEL_3, ADC_ATTEN_DB_12); /* GPIO 7 */
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "ADC initialized");
|
||||
}
|
||||
|
||||
void app_init_nvs(void)
|
||||
{
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
ESP_LOGI(TAG, "NVS initialized");
|
||||
}
|
||||
|
||||
void app_create_tasks(void)
|
||||
{
|
||||
/* Create queues */
|
||||
adc_data_queue = xQueueCreate(2, sizeof(adc_data_t));
|
||||
ui_event_queue = xQueueCreate(10, sizeof(ui_event_t));
|
||||
|
||||
/* Create tasks */
|
||||
xTaskCreatePinnedToCore(adc_task, "adc_task", TASK_STACK_ADC, NULL,
|
||||
TASK_PRIO_ADC, NULL, tskNO_AFFINITY);
|
||||
xTaskCreatePinnedToCore(ui_task, "ui_task", TASK_STACK_UI, NULL,
|
||||
TASK_PRIO_UI, NULL, tskNO_AFFINITY);
|
||||
xTaskCreatePinnedToCore(rf_task, "rf_task", TASK_STACK_RF, NULL,
|
||||
TASK_PRIO_RF, NULL, tskNO_AFFINITY);
|
||||
|
||||
ESP_LOGI(TAG, "Tasks created");
|
||||
}
|
||||
|
||||
void app_init(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing AliceRC...");
|
||||
|
||||
app_init_nvs();
|
||||
app_init_gpio();
|
||||
app_init_adc();
|
||||
app_create_tasks();
|
||||
|
||||
ESP_LOGI(TAG, "AliceRC initialized successfully");
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @file app_main.h
|
||||
* @brief AliceRC application header
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* GPIO Pin Mapping - ESP32S3 */
|
||||
/* ADC Channels */
|
||||
#define ADC_CH1_GPIO 4
|
||||
#define ADC_CH2_GPIO 5
|
||||
#define ADC_CH3_GPIO 6
|
||||
#define ADC_CH4_GPIO 7
|
||||
#define ADC_CH5_GPIO 15
|
||||
#define ADC_CH6_GPIO 16
|
||||
#define ADC_CH7_GPIO 17
|
||||
#define ADC_CH8_GPIO 9
|
||||
#define ADC_BAT_GPIO 8
|
||||
|
||||
/* NRF24L01 - ESP32S3 */
|
||||
#define NRF_MOSI_GPIO 1
|
||||
#define NRF_MISO_GPIO 40
|
||||
#define NRF_SCLK_GPIO 39
|
||||
#define NRF_CS_GPIO 38
|
||||
#define NRF_CE_GPIO 2
|
||||
#define NRF_IRQ_GPIO 42
|
||||
|
||||
/* LLCC68 (E220) - ESP32S3 only */
|
||||
#define LLCC_CS_GPIO 10
|
||||
#define LLCC_SCLK_GPIO 11
|
||||
#define LLCC_MISO_GPIO 12
|
||||
#define LLCC_MOSI_GPIO 13
|
||||
#define LLCC_NRST_GPIO 14
|
||||
#define LLCC_BUSY_GPIO 45
|
||||
#define LLCC_IRQ_GPIO 45 /* shared with BUSY? Check datasheet */
|
||||
|
||||
/* OLED SSD1315 - I2C */
|
||||
#define OLED_SCL_GPIO 37
|
||||
#define OLED_SDA_GPIO 36
|
||||
|
||||
/* Buttons */
|
||||
#define BTN_UP_GPIO 35
|
||||
#define BTN_DOWN_GPIO 48
|
||||
#define BTN_SEL_GPIO 47
|
||||
#define BTN_BACK_GPIO 3
|
||||
|
||||
/* Other peripherals */
|
||||
#define USB_DM_GPIO 19
|
||||
#define USB_DP_GPIO 20
|
||||
#define AUDIO_PWM_GPIO 26 /* ESP32S3: PWM audio */
|
||||
#define AUDIO_DAC_GPIO 26 /* ESP32: DAC audio */
|
||||
#define PWR_EN_GPIO 41
|
||||
#define STATUS_LED_GPIO 0
|
||||
|
||||
/* ESP32 (basic) GPIO mapping */
|
||||
/* ADC Channels */
|
||||
#define ESP32_ADC_CH1_GPIO 36
|
||||
#define ESP32_ADC_CH2_GPIO 39
|
||||
#define ESP32_ADC_CH3_GPIO 34
|
||||
#define ESP32_ADC_CH4_GPIO 35
|
||||
#define ESP32_ADC_CH5_GPIO 32
|
||||
#define ESP32_ADC_CH6_GPIO 33
|
||||
#define ESP32_ADC_CH7_GPIO 25
|
||||
#define ESP32_ADC_CH8_GPIO 13
|
||||
|
||||
/* NRF24L01 - ESP32 */
|
||||
#define ESP32_NRF_MOSI_GPIO 23
|
||||
#define ESP32_NRF_MISO_GPIO 19
|
||||
#define ESP32_NRF_SCLK_GPIO 18
|
||||
#define ESP32_NRF_CS_GPIO 5
|
||||
#define ESP32_NRF_CE_GPIO 22
|
||||
#define ESP32_NRF_IRQ_GPIO 21
|
||||
|
||||
/* OLED - ESP32 */
|
||||
#define ESP32_OLED_SCL_GPIO 17
|
||||
#define ESP32_OLED_SDA_GPIO 16
|
||||
|
||||
/* Buttons - ESP32 */
|
||||
#define ESP32_BTN_UP_GPIO 4
|
||||
#define ESP32_BTN_DOWN_GPIO 2
|
||||
#define ESP32_BTN_SEL_GPIO 15
|
||||
|
||||
/* Battery */
|
||||
#define BAT_ADC_CHANNEL ADC_CHANNEL_0
|
||||
#define BAT_DIVIDER_RATIO 2.0f /* Voltage divider ratio */
|
||||
#define BAT_FULL_MV 4200 /* Full battery mV */
|
||||
#define BAT_EMPTY_MV 3200 /* Empty battery mV */
|
||||
|
||||
/* Task priorities */
|
||||
#define TASK_PRIO_RF 5
|
||||
#define TASK_PRIO_UI 4
|
||||
#define TASK_PRIO_ADC 3
|
||||
#define TASK_PRIO_AUDIO 2
|
||||
|
||||
/* Task stack sizes */
|
||||
#define TASK_STACK_RF 4096
|
||||
#define TASK_STACK_UI 4096
|
||||
#define TASK_STACK_ADC 2048
|
||||
#define TASK_STACK_AUDIO 2048
|
||||
|
||||
/* Function declarations */
|
||||
void app_init(void);
|
||||
void app_init_gpio(void);
|
||||
void app_create_tasks(void);
|
||||
uint8_t battery_get_percent(uint16_t mv);
|
||||
|
||||
/* Data type definitions */
|
||||
typedef struct adc_data_s {
|
||||
uint16_t channels[8];
|
||||
uint16_t battery_mv;
|
||||
} adc_data_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t key; /* KEY_UP, KEY_DOWN, KEY_SEL, KEY_BACK */
|
||||
uint8_t action; /* 0=press, 1=release, 2=long_press */
|
||||
} ui_event_t;
|
||||
|
||||
/* Queue handles (defined in app_main.c) */
|
||||
extern QueueHandle_t adc_data_queue;
|
||||
extern QueueHandle_t rf_command_queue;
|
||||
extern QueueHandle_t ui_event_queue;
|
||||
|
||||
/* RF task */
|
||||
void rf_task(void *pvParameters);
|
||||
/* UI task */
|
||||
void ui_task(void *pvParameters);
|
||||
/* ADC task */
|
||||
void adc_task(void *pvParameters);
|
||||
/* Audio task */
|
||||
void audio_task(void *pvParameters);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @file battery.c
|
||||
* @brief Battery monitoring utilities
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "app_main.h"
|
||||
|
||||
static const char *TAG = "battery";
|
||||
|
||||
/* Battery percentage lookup table (simple linear for now) */
|
||||
uint8_t battery_get_percent(uint16_t mv)
|
||||
{
|
||||
if (mv >= BAT_FULL_MV) return 100;
|
||||
if (mv <= BAT_EMPTY_MV) return 0;
|
||||
|
||||
/* Linear interpolation between EMPTY and FULL */
|
||||
return (uint8_t)((mv - BAT_EMPTY_MV) * 100 / (BAT_FULL_MV - BAT_EMPTY_MV));
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @file led.c
|
||||
* @brief Status LED control
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
#include "app_main.h"
|
||||
|
||||
static const char *TAG = "led";
|
||||
|
||||
void led_set(uint8_t on)
|
||||
{
|
||||
gpio_set_level(STATUS_LED_GPIO, on ? 1 : 0);
|
||||
}
|
||||
|
||||
void led_blink(int times, int delay_ms)
|
||||
{
|
||||
for (int i = 0; i < times; i++) {
|
||||
led_set(1);
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
led_set(0);
|
||||
if (i < times - 1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @file main.c
|
||||
* @brief AliceRC ESP32/ESP32S3 遥控器主程序入口
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "app_main.h"
|
||||
|
||||
static const char *TAG = "AliceRC";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "AliceRC Transmitter v1.0 starting...");
|
||||
|
||||
// Initialize all subsystems
|
||||
app_init();
|
||||
|
||||
// Main loop runs in app_main_task
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @file rf_task.c
|
||||
* @brief RF task - NRF24L01 communication
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "app_main.h"
|
||||
|
||||
static const char *TAG = "rf";
|
||||
|
||||
/* NRF24L01 protocol constants (matching AliceRX_STC) */
|
||||
#define FRAME_ID_CHANNELS 0xA0
|
||||
#define FRAME_ID_TELEMETRY 0xB0
|
||||
#define RF_PAYLOAD_SIZE 32
|
||||
#define RF_CHANNEL_DEFAULT 100
|
||||
#define RF_DATA_RATE RF_1MBPS
|
||||
#define RF_TX_POWER RF_PA_MAX
|
||||
|
||||
/* RF frame structure */
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t frame_id; /* 0xA0 = FRAME_ID_CHANNELS */
|
||||
uint8_t seq; /* Sequence number */
|
||||
uint8_t model_id; /* Model ID */
|
||||
uint8_t flags; /* Flags */
|
||||
uint8_t sbus_data[22]; /* 16 channels × 11bit = 22 bytes little-endian bitstream */
|
||||
uint8_t reserved[6]; /* Reserved */
|
||||
} rf_payload_t;
|
||||
|
||||
/* Telemetry data from receiver */
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t frame_id; /* 0xB0 = FRAME_ID_TELEMETRY */
|
||||
uint8_t seq; /* Sequence number */
|
||||
uint16_t battery_mv; /* Battery voltage (big-endian, mV) */
|
||||
uint8_t reserved[28]; /* Reserved */
|
||||
} rf_telemetry_t;
|
||||
|
||||
/* RF state */
|
||||
static uint8_t rf_seq = 0;
|
||||
static uint8_t rf_model_id = 0;
|
||||
|
||||
void rf_send_channels(adc_data_t *adc_data)
|
||||
{
|
||||
rf_payload_t payload;
|
||||
memset(&payload, 0, sizeof(payload));
|
||||
|
||||
payload.frame_id = FRAME_ID_CHANNELS;
|
||||
payload.seq = rf_seq++;
|
||||
payload.model_id = rf_model_id;
|
||||
|
||||
/* Convert 8 ADC channels to SBUS-like 11-bit format */
|
||||
/* Map 12-bit ADC (0-4095) to SBUS range (200-1844) */
|
||||
uint16_t sbus_channels[16] = {0};
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint32_t val = adc_data->channels[i];
|
||||
/* Map 0-4095 to 200-1844 */
|
||||
sbus_channels[i] = 200 + (val * 1644 / 4096);
|
||||
if (sbus_channels[i] > 1844) sbus_channels[i] = 1844;
|
||||
}
|
||||
/* Center unused channels */
|
||||
for (int i = 8; i < 16; i++) {
|
||||
sbus_channels[i] = 1024;
|
||||
}
|
||||
|
||||
/* Pack 16 × 11bit into 22 bytes little-endian */
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint16_t ch = sbus_channels[i];
|
||||
int byte_idx = (i * 11) / 8;
|
||||
int bit_off = (i * 11) % 8;
|
||||
payload.sbus_data[byte_idx] |= (ch << bit_off) & 0xFF;
|
||||
payload.sbus_data[byte_idx + 1] |= (ch >> (8 - bit_off)) & 0xFF;
|
||||
if (bit_off > 5) {
|
||||
payload.sbus_data[byte_idx + 2] |= (ch >> (16 - bit_off)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Send via NRF24L01 hardware */
|
||||
ESP_LOGD(TAG, "RF TX seq=%d ch1=%d", payload.seq, sbus_channels[0]);
|
||||
}
|
||||
|
||||
void rf_task(void *pvParameters)
|
||||
{
|
||||
ESP_LOGI(TAG, "RF task started");
|
||||
|
||||
/* TODO: Initialize NRF24L01 hardware */
|
||||
/* nrf24_init(); */
|
||||
/* nrf24_set_channel(100); */
|
||||
/* nrf24_set_data_rate(RF_DATA_RATE); */
|
||||
/* nrf24_set_power(RF_TX_POWER); */
|
||||
|
||||
while (1) {
|
||||
/* Read latest ADC data */
|
||||
adc_data_t adc_data;
|
||||
if (adc_data_queue && xQueuePeek(adc_data_queue, &adc_data, 0)) {
|
||||
/* Send channel data over RF */
|
||||
rf_send_channels(&adc_data);
|
||||
}
|
||||
|
||||
/* TODO: Check for ACK/telemetry from receiver */
|
||||
/* rf_telemetry_t telemetry; */
|
||||
/* if (nrf24_read_ack(&telemetry)) { */
|
||||
/* linked = 1; */
|
||||
/* lost_count = 0; */
|
||||
/* } else { */
|
||||
/* if (++lost_count > 50) linked = 0; */
|
||||
/* } */
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); /* 50Hz RF update rate */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @file ui_task.c
|
||||
* @brief UI task - button handling and OLED display
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "app_main.h"
|
||||
|
||||
static const char *TAG = "ui";
|
||||
|
||||
/* Button debounce parameters */
|
||||
#define DEBOUNCE_MS 50
|
||||
#define LONG_PRESS_MS 1000
|
||||
|
||||
/* Button state tracking */
|
||||
typedef struct {
|
||||
uint8_t gpio;
|
||||
uint8_t last_state;
|
||||
uint32_t last_change_ms;
|
||||
uint32_t press_start_ms;
|
||||
uint8_t long_press_sent;
|
||||
} btn_state_t;
|
||||
|
||||
static btn_state_t buttons[] = {
|
||||
{BTN_UP_GPIO, 1, 0, 0, 0},
|
||||
{BTN_DOWN_GPIO, 1, 0, 0, 0},
|
||||
{BTN_SEL_GPIO, 1, 0, 0, 0},
|
||||
{BTN_BACK_GPIO, 1, 0, 0, 0},
|
||||
};
|
||||
|
||||
static void handle_button(btn_state_t *btn, uint8_t idx)
|
||||
{
|
||||
uint8_t state = gpio_get_level(btn->gpio);
|
||||
uint32_t now = xTaskGetTickCount() * portTICK_PERIOD_MS;
|
||||
|
||||
if (state != btn->last_state) {
|
||||
btn->last_change_ms = now;
|
||||
btn->last_state = state;
|
||||
|
||||
if (state == 0) { /* Pressed */
|
||||
btn->press_start_ms = now;
|
||||
btn->long_press_sent = 0;
|
||||
|
||||
ui_event_t evt = {.key = idx, .action = 0}; /* press */
|
||||
if (ui_event_queue) {
|
||||
xQueueSend(ui_event_queue, &evt, 0);
|
||||
}
|
||||
} else { /* Released */
|
||||
if (!btn->long_press_sent) {
|
||||
ui_event_t evt = {.key = idx, .action = 1}; /* release */
|
||||
if (ui_event_queue) {
|
||||
xQueueSend(ui_event_queue, &evt, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (state == 0 && !btn->long_press_sent) {
|
||||
/* Check for long press */
|
||||
if ((now - btn->press_start_ms) >= LONG_PRESS_MS) {
|
||||
btn->long_press_sent = 1;
|
||||
ui_event_t evt = {.key = idx, .action = 2}; /* long press */
|
||||
if (ui_event_queue) {
|
||||
xQueueSend(ui_event_queue, &evt, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_task(void *pvParameters)
|
||||
{
|
||||
ESP_LOGI(TAG, "UI task started");
|
||||
|
||||
/* OLED initialization would go here */
|
||||
/* display_init(); */
|
||||
|
||||
/* Button state initialization */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buttons[i].last_state = gpio_get_level(buttons[i].gpio);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
/* Scan buttons */
|
||||
for (int i = 0; i < 4; i++) {
|
||||
handle_button(&buttons[i], i);
|
||||
}
|
||||
|
||||
/* Process UI events */
|
||||
ui_event_t evt;
|
||||
while (xQueueReceive(ui_event_queue, &evt, 0)) {
|
||||
const char *key_names[] = {"UP", "DOWN", "SEL", "BACK"};
|
||||
const char *actions[] = {"press", "release", "long_press"};
|
||||
ESP_LOGI(TAG, "Button: %s - %s", key_names[evt.key], actions[evt.action]);
|
||||
}
|
||||
|
||||
/* Read ADC data for display */
|
||||
adc_data_t adc_data;
|
||||
if (adc_data_queue && xQueuePeek(adc_data_queue, &adc_data, 0)) {
|
||||
/* Update OLED display with ADC data */
|
||||
/* display_update(&adc_data); */
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(20)); /* 50Hz scan rate */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user