【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
前面,我们学习了屏幕驱动,学习了lvgl,也学习了触摸屏。但是很多开发,尤其是成本要求很高的场景,本身其实是用不了触摸屏的。这个时候,那就只有一个办法,屏幕配合按键使用。要是加上蜂鸣器和led,就是一个仿真的电报发射系统。问题来了,该怎么使用呢?

1、简单的场景直接创建任务操作
如果应用比较简单,通常情况下,就是直接开启一个task,这个task循环读取key的状态,然后更新控件的状态。这是简单场景的处理方式。
2、复杂场景先进状态机再更新界面
如果应用稍微复杂一点,比如一个应用达到几千行代码了,这个时候再用一个task来处理,就不合适了。这种情况下比较适合的做法,就是按键事件先送到状态机,状态机处理完了,然后送到一个专门的线程更新界面。这样界面归界面,程序归程序,比较简洁一点。
3、一定要动手去做实验
虽然这个应用不复杂,但是真的去做的话,还是会遇到问题。比如,很有可能按键电路会搭错。这种上拉的按键电路知道是一回事,画出来是一回事,用面包板搭出来又是另外一回事。所以,大家一定不要怕麻烦,还是要多搭建电路,利用问题去逼着自己成长。
4、用ai先写按键代码
走到这里,其实就可以让ai帮我们写代码。可以在之前一节的基础上编写,比如通知ai,编写一个用d25做按键的代码,按下去的时候,需要对label做自增处理,这样就可以得到下面这些内容,
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_task_wdt.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "lvgl.h"
//================================================
// ILI9341 CONFIG
//================================================
#define ILI9341_SPI_HOST SPI2_HOST
#define ILI9341_SCLK_PIN 14
#define ILI9341_MOSI_PIN 13
#define ILI9341_CS_PIN 15
#define ILI9341_DC_PIN 2
#define ILI9341_RST_PIN 4
#define ILI9341_BL_PIN 12
#define ILI9341_WIDTH 320
#define ILI9341_HEIGHT 240
//================================================
// XPT2046 TOUCH CONFIG
//================================================
#define XPT2046_SPI_HOST SPI3_HOST
#define XPT2046_SCLK_PIN 18
#define XPT2046_MOSI_PIN 19
#define XPT2046_MISO_PIN 21
#define XPT2046_CS_PIN 5
#define XPT2046_IRQ_PIN 22
#define XPT2046_CMD_X 0xD0
#define XPT2046_CMD_Y 0x90
#define TOUCH_THRESHOLD 50
//================================================
// D25 BUTTON CONFIG
//================================================
#define D25_BUTTON_PIN 25
#define DEBOUNCE_MS 50 // Debounce time 50ms
static spi_device_handle_t ili9341_spi;
static spi_device_handle_t xpt2046_spi;
static SemaphoreHandle_t lvgl_mutex;
static lv_obj_t *label_count;
static lv_obj_t *btn_increment;
static int counter = 0;
// D25 button state variables
static bool d25_button_last_state = true; // Last state (high = not pressed due to pull-up)
static TickType_t d25_button_last_tick = 0; // Timestamp of last state change
//================================================
// LCD SPI LOW LEVEL
//================================================
static void ili9341_send_cmd(uint8_t cmd)
{
gpio_set_level(ILI9341_DC_PIN, 0);
spi_transaction_t t = {
.length = 8,
.tx_buffer = &cmd
};
spi_device_transmit(ili9341_spi, &t);
}
static void ili9341_send_data(const uint8_t *data, size_t len)
{
gpio_set_level(ILI9341_DC_PIN, 1);
spi_transaction_t t = {
.length = len * 8,
.tx_buffer = data
};
spi_device_transmit(ili9341_spi, &t);
}
static void ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
ili9341_send_cmd(0x2A);
uint8_t col[] = {x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF};
ili9341_send_data(col, 4);
ili9341_send_cmd(0x2B);
uint8_t row[] = {y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF};
ili9341_send_data(row, 4);
ili9341_send_cmd(0x2C);
}
static void ili9341_flush_cb(lv_disp_drv_t *drv,
const lv_area_t *area,
lv_color_t *color_map)
{
uint32_t w = area->x2 - area->x1 + 1;
uint32_t h = area->y2 - area->y1 + 1;
ili9341_set_window(area->x1, area->y1, area->x2, area->y2);
gpio_set_level(ILI9341_DC_PIN, 1);
spi_transaction_t t = {
.length = w * h * 16,
.tx_buffer = color_map
};
spi_device_transmit(ili9341_spi, &t);
lv_disp_flush_ready(drv);
}
//================================================
// LCD INIT
//================================================
static void ili9341_init(void)
{
gpio_set_level(ILI9341_RST_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(ILI9341_RST_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x01); // Software reset
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x11); // Exit sleep mode
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x3A); // Set pixel format
ili9341_send_data((uint8_t[]){0x55}, 1); // 16-bit color
ili9341_send_cmd(0x36); // Set memory access control
ili9341_send_data((uint8_t[]){0x28}, 1);
ili9341_send_cmd(0x29); // Display on
gpio_set_level(ILI9341_BL_PIN, 1); // Turn on backlight
}
//================================================
// SPI INIT LCD
//================================================
static void ili9341_spi_init(void)
{
gpio_config_t io = {
.pin_bit_mask =
(1ULL << ILI9341_DC_PIN) |
(1ULL << ILI9341_RST_PIN) |
(1ULL << ILI9341_BL_PIN),
.mode = GPIO_MODE_OUTPUT
};
gpio_config(&io);
spi_bus_config_t buscfg = {
.mosi_io_num = ILI9341_MOSI_PIN,
.miso_io_num = -1,
.sclk_io_num = ILI9341_SCLK_PIN,
.max_transfer_sz = ILI9341_WIDTH * ILI9341_HEIGHT * 2
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 20 * 1000 * 1000,
.mode = 0,
.spics_io_num = ILI9341_CS_PIN,
.queue_size = 7
};
spi_bus_initialize(ILI9341_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_bus_add_device(ILI9341_SPI_HOST, &devcfg, &ili9341_spi);
}
//================================================
// SPI INIT TOUCH
//================================================
static void xpt2046_spi_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << XPT2046_IRQ_PIN),
.mode = GPIO_MODE_INPUT
};
gpio_config(&io);
spi_bus_config_t buscfg = {
.mosi_io_num = XPT2046_MOSI_PIN,
.miso_io_num = XPT2046_MISO_PIN,
.sclk_io_num = XPT2046_SCLK_PIN,
.max_transfer_sz = 8
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 2 * 1000 * 1000,
.mode = 0,
.spics_io_num = XPT2046_CS_PIN,
.queue_size = 1
};
spi_bus_initialize(XPT2046_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_bus_add_device(XPT2046_SPI_HOST, &devcfg, &xpt2046_spi);
}
//================================================
// D25 BUTTON INIT
//================================================
static void d25_button_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << D25_BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE, // Enable internal pull-up
.pull_down_en = GPIO_PULLDOWN_DISABLE
};
gpio_config(&io);
// Read initial state (high = not pressed)
d25_button_last_state = gpio_get_level(D25_BUTTON_PIN);
d25_button_last_tick = xTaskGetTickCount();
}
//================================================
// D25 BUTTON READ WITH DEBOUNCE
//================================================
static bool d25_button_is_pressed(void)
{
bool current_state = gpio_get_level(D25_BUTTON_PIN);
TickType_t current_tick = xTaskGetTickCount();
// Detect level change
if (current_state != d25_button_last_state) {
// Record change timestamp
d25_button_last_tick = current_tick;
d25_button_last_state = current_state;
return false;
}
// Level stable and duration exceeds debounce time
if ((current_tick - d25_button_last_tick) >= pdMS_TO_TICKS(DEBOUNCE_MS)) {
// Low level = pressed (active low)
if (current_state == 0) {
return true;
}
}
return false;
}
//================================================
// TOUCH READ
//================================================
static uint16_t xpt2046_read_adc(uint8_t cmd)
{
uint8_t tx[3] = {cmd, 0, 0};
uint8_t rx[3] = {0};
spi_transaction_t t = {
.length = 24,
.tx_buffer = tx,
.rx_buffer = rx
};
spi_device_transmit(xpt2046_spi, &t);
return ((rx[1] << 8) | rx[2]) >> 3 & 0x0FFF;
}
static bool xpt2046_read_touch(uint16_t *x, uint16_t *y)
{
if (gpio_get_level(XPT2046_IRQ_PIN) == 1) {
return false; // No touch detected
}
uint16_t raw_x = xpt2046_read_adc(XPT2046_CMD_X);
uint16_t raw_y = xpt2046_read_adc(XPT2046_CMD_Y);
uint16_t z1 = xpt2046_read_adc(0xB0);
if (z1 < TOUCH_THRESHOLD) {
return false; // Pressure too light
}
// Map raw ADC values to screen coordinates
*x = (raw_x * ILI9341_WIDTH) / 4095;
*y = (raw_y * ILI9341_HEIGHT) / 4095;
// Clamp values to screen boundaries
if (*x >= ILI9341_WIDTH) *x = ILI9341_WIDTH - 1;
if (*y >= ILI9341_HEIGHT) *y = ILI9341_HEIGHT - 1;
return true;
}
//================================================
// TOUCH LVGL CALLBACK
//================================================
static void touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
uint16_t x, y;
if (xpt2046_read_touch(&x, &y)) {
data->state = LV_INDEV_STATE_PRESSED;
data->point.x = x;
data->point.y = y;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
//================================================
// UI
//================================================
static void btn_event_cb(lv_event_t *e)
{
if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY)) {
counter++;
char buf[32];
sprintf(buf, "Count: %d", counter);
lv_label_set_text(label_count, buf);
xSemaphoreGive(lvgl_mutex);
}
}
}
static void ui_create(void)
{
// Set background color to white
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), 0);
// Create counter label
label_count = lv_label_create(lv_scr_act());
lv_label_set_text(label_count, "Count: 0");
lv_obj_align(label_count, LV_ALIGN_CENTER, 0, -40);
// Create increment button
btn_increment = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn_increment, 120, 50);
lv_obj_align(btn_increment, LV_ALIGN_CENTER, 0, 40);
// Create button label
lv_obj_t *lbl = lv_label_create(btn_increment);
lv_label_set_text(lbl, "Press");
lv_obj_center(lbl);
// Register button click event
lv_obj_add_event_cb(btn_increment, btn_event_cb, LV_EVENT_CLICKED, NULL);
}
//================================================
// LVGL TICK
//================================================
static void lv_tick_cb(void *arg)
{
lv_tick_inc(1);
}
//================================================
// D25 BUTTON TASK
//================================================
static void d25_button_task(void *pvParameter)
{
while (1) {
// Check if D25 button is pressed (with debounce)
if (d25_button_is_pressed()) {
printf("D25 Button pressed!\n");
// Acquire LVGL mutex and update counter
if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY)) {
counter++;
char buf[40];
sprintf(buf, "Count: %d", counter);
lv_label_set_text(label_count, buf);
xSemaphoreGive(lvgl_mutex);
}
// Prevent multiple triggers from a single press
vTaskDelay(pdMS_TO_TICKS(200));
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
//================================================
// MAIN
//================================================
void app_main(void)
{
printf("START\n");
// Create mutex for LVGL operations
lvgl_mutex = xSemaphoreCreateMutex();
// Initialize LCD SPI and display
ili9341_spi_init();
ili9341_init();
// Initialize touch controller SPI
xpt2046_spi_init();
// Initialize D25 button
d25_button_init();
// Initialize LVGL
lv_init();
// Create timer for LVGL tick
esp_timer_handle_t timer;
esp_timer_create_args_t args = {
.callback = lv_tick_cb,
.name = "lv_tick"
};
esp_timer_create(&args, &timer);
esp_timer_start_periodic(timer, 1000); // 1ms tick
// Initialize display buffer
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[ILI9341_WIDTH * 20];
static lv_color_t buf2[ILI9341_WIDTH * 20];
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, ILI9341_WIDTH * 20);
// Register display driver
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = ILI9341_WIDTH;
disp_drv.ver_res = ILI9341_HEIGHT;
disp_drv.flush_cb = ili9341_flush_cb;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
// Register touch input driver
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touch_cb;
lv_indev_drv_register(&indev_drv);
// Create UI
ui_create();
// Create D25 button detection task
xTaskCreate(d25_button_task, "d25_button_task", 2048, NULL, 5, NULL);
// Add main task to watchdog
esp_task_wdt_add(NULL);
// Main loop
while (1) {
esp_task_wdt_reset(); // Reset watchdog
lv_timer_handler(); // Handle LVGL tasks
vTaskDelay(pdMS_TO_TICKS(10));
}
}
5、编译和测试
代码写好后,直接copy过去编一下。因为本身代码内容就是ok的,让ai添加的内容不多,所以生成的代码很容易是对的。接着,直接编译烧录即可。如果跑起来没什么问题,就可以按键按下去试试了。
功能没问题的情况下,再看看逻辑对不对。
6、再加上蜂鸣器
前面我们已经有屏幕、触摸屏和按键了,试想一下,再加上d26蜂鸣器呢?即,按下去的时候,蜂鸣器响,松开的时候,停止。当然这个时候,如果继续让ai做,也是可以的。但是,我们会发现代码到达一定的行数时,ai会摆烂,国内国外的ai都是如此。或许是能力不行,或许是市场策略的考虑,用商业ai模型或许好一点。这个时候,就需要自己在ai基础上,稍微修改一下代码,其实也是可以的。比如,我们可以这么修改,
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_task_wdt.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "lvgl.h"
//================================================
// ILI9341 CONFIG
//================================================
#define ILI9341_SPI_HOST SPI2_HOST
#define ILI9341_SCLK_PIN 14
#define ILI9341_MOSI_PIN 13
#define ILI9341_CS_PIN 15
#define ILI9341_DC_PIN 2
#define ILI9341_RST_PIN 4
#define ILI9341_BL_PIN 12
#define ILI9341_WIDTH 320
#define ILI9341_HEIGHT 240
//================================================
// XPT2046 TOUCH CONFIG
//================================================
#define XPT2046_SPI_HOST SPI3_HOST
#define XPT2046_SCLK_PIN 18
#define XPT2046_MOSI_PIN 19
#define XPT2046_MISO_PIN 21
#define XPT2046_CS_PIN 5
#define XPT2046_IRQ_PIN 22
#define XPT2046_CMD_X 0xD0
#define XPT2046_CMD_Y 0x90
#define TOUCH_THRESHOLD 50
//================================================
// D25 / D26 CONFIG
//================================================
#define D25_BUTTON_PIN 25
#define D26_BUZZER_PIN 26
#define DEBOUNCE_MS 50
//================================================
// GLOBAL
//================================================
static spi_device_handle_t ili9341_spi;
static spi_device_handle_t xpt2046_spi;
static SemaphoreHandle_t lvgl_mutex;
static lv_obj_t *label_count;
static lv_obj_t *btn_increment;
static int counter = 0;
static bool d25_button_last_state = true;
static TickType_t d25_button_last_tick = 0;
//================================================
// LCD LOW LEVEL
//================================================
static void ili9341_send_cmd(uint8_t cmd)
{
gpio_set_level(ILI9341_DC_PIN, 0);
spi_transaction_t t = {
.length = 8,
.tx_buffer = &cmd
};
spi_device_transmit(ili9341_spi, &t);
}
static void ili9341_send_data(const uint8_t *data, size_t len)
{
gpio_set_level(ILI9341_DC_PIN, 1);
spi_transaction_t t = {
.length = len * 8,
.tx_buffer = data
};
spi_device_transmit(ili9341_spi, &t);
}
static void ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
ili9341_send_cmd(0x2A);
uint8_t col[] = {x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF};
ili9341_send_data(col, 4);
ili9341_send_cmd(0x2B);
uint8_t row[] = {y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF};
ili9341_send_data(row, 4);
ili9341_send_cmd(0x2C);
}
static void ili9341_flush_cb(lv_disp_drv_t *drv,
const lv_area_t *area,
lv_color_t *color_map)
{
uint32_t w = area->x2 - area->x1 + 1;
uint32_t h = area->y2 - area->y1 + 1;
ili9341_set_window(area->x1, area->y1, area->x2, area->y2);
gpio_set_level(ILI9341_DC_PIN, 1);
spi_transaction_t t = {
.length = w * h * 16,
.tx_buffer = color_map
};
spi_device_transmit(ili9341_spi, &t);
lv_disp_flush_ready(drv);
}
//================================================
// LCD INIT
//================================================
static void ili9341_init(void)
{
gpio_set_level(ILI9341_RST_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(ILI9341_RST_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x11);
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x3A);
ili9341_send_data((uint8_t[]){0x55}, 1);
ili9341_send_cmd(0x36);
ili9341_send_data((uint8_t[]){0x28}, 1);
ili9341_send_cmd(0x29);
gpio_set_level(ILI9341_BL_PIN, 1);
}
//================================================
// SPI LCD INIT
//================================================
static void ili9341_spi_init(void)
{
gpio_config_t io = {
.pin_bit_mask =
(1ULL << ILI9341_DC_PIN) |
(1ULL << ILI9341_RST_PIN) |
(1ULL << ILI9341_BL_PIN),
.mode = GPIO_MODE_OUTPUT
};
gpio_config(&io);
spi_bus_config_t buscfg = {
.mosi_io_num = ILI9341_MOSI_PIN,
.miso_io_num = -1,
.sclk_io_num = ILI9341_SCLK_PIN,
.max_transfer_sz = ILI9341_WIDTH * ILI9341_HEIGHT * 2
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 20 * 1000 * 1000,
.mode = 0,
.spics_io_num = ILI9341_CS_PIN,
.queue_size = 7
};
spi_bus_initialize(ILI9341_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_bus_add_device(ILI9341_SPI_HOST, &devcfg, &ili9341_spi);
}
//================================================
// TOUCH INIT
//================================================
static void xpt2046_spi_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << XPT2046_IRQ_PIN),
.mode = GPIO_MODE_INPUT
};
gpio_config(&io);
spi_bus_config_t buscfg = {
.mosi_io_num = XPT2046_MOSI_PIN,
.miso_io_num = XPT2046_MISO_PIN,
.sclk_io_num = XPT2046_SCLK_PIN,
.max_transfer_sz = 8
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 2 * 1000 * 1000,
.mode = 0,
.spics_io_num = XPT2046_CS_PIN,
.queue_size = 1
};
spi_bus_initialize(XPT2046_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_bus_add_device(XPT2046_SPI_HOST, &devcfg, &xpt2046_spi);
}
//================================================
// BUZZER INIT (D26)
//================================================
static void buzzer_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << D26_BUZZER_PIN),
.mode = GPIO_MODE_OUTPUT
};
gpio_config(&io);
gpio_set_level(D26_BUZZER_PIN, 1);
}
//================================================
// D25 BUTTON
//================================================
static void d25_button_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << D25_BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE
};
gpio_config(&io);
d25_button_last_state = gpio_get_level(D25_BUTTON_PIN);
d25_button_last_tick = xTaskGetTickCount();
}
//================================================
// TOUCH
//================================================
static uint16_t xpt2046_read_adc(uint8_t cmd)
{
uint8_t tx[3] = {cmd, 0, 0};
uint8_t rx[3] = {0};
spi_transaction_t t = {
.length = 24,
.tx_buffer = tx,
.rx_buffer = rx
};
spi_device_transmit(xpt2046_spi, &t);
return ((rx[1] << 8) | rx[2]) >> 3;
}
static bool xpt2046_read_touch(uint16_t *x, uint16_t *y)
{
if (gpio_get_level(XPT2046_IRQ_PIN)) return false;
uint16_t raw_x = xpt2046_read_adc(XPT2046_CMD_X);
uint16_t raw_y = xpt2046_read_adc(XPT2046_CMD_Y);
*x = raw_x * ILI9341_WIDTH / 4095;
*y = raw_y * ILI9341_HEIGHT / 4095;
return true;
}
//================================================
// LVGL TOUCH
//================================================
static void touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
uint16_t x, y;
if (xpt2046_read_touch(&x, &y)) {
data->state = LV_INDEV_STATE_PRESSED;
data->point.x = x;
data->point.y = y;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
//================================================
// UI
//================================================
static void btn_event_cb(lv_event_t *e)
{
if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY)) {
counter++;
char buf[32];
sprintf(buf, "Count: %d", counter);
lv_label_set_text(label_count, buf);
xSemaphoreGive(lvgl_mutex);
}
}
}
//================================================
// UI
//================================================
static void ui_create(void)
{
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), 0);
label_count = lv_label_create(lv_scr_act());
lv_label_set_text(label_count, "Count: 0");
lv_obj_align(label_count, LV_ALIGN_CENTER, 0, -40);
btn_increment = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn_increment, 120, 50);
lv_obj_align(btn_increment, LV_ALIGN_CENTER, 0, 40);
lv_obj_t *lbl = lv_label_create(btn_increment);
lv_label_set_text(lbl, "Press");
lv_obj_center(lbl);
// Register button click event
lv_obj_add_event_cb(btn_increment, btn_event_cb, LV_EVENT_CLICKED, NULL);
}
//================================================
// LVGL TICK
//================================================
static void lv_tick_cb(void *arg)
{
lv_tick_inc(1);
}
//================================================
// D25 TASK + BUZZER CONTROL
//================================================
static void d25_button_task(void *arg)
{
bool last_state = 1;
while (1) {
bool cur = gpio_get_level(D25_BUTTON_PIN);
if (last_state == 1 && cur == 0) {
gpio_set_level(D26_BUZZER_PIN, 0);
printf("PRESS\n");
if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY)) {
counter++;
char buf[32];
sprintf(buf, "Count: %d", counter);
lv_label_set_text(label_count, buf);
xSemaphoreGive(lvgl_mutex);
}
}
if (last_state == 0 && cur == 1) {
gpio_set_level(D26_BUZZER_PIN, 1);
printf("RELEASE\n");
}
last_state = cur;
vTaskDelay(pdMS_TO_TICKS(10));
}
}
//================================================
// MAIN
//================================================
void app_main(void)
{
printf("START\n");
lvgl_mutex = xSemaphoreCreateMutex();
ili9341_spi_init();
ili9341_init();
xpt2046_spi_init();
d25_button_init();
buzzer_init(); // <<< 新增
lv_init();
esp_timer_handle_t timer;
esp_timer_create_args_t args = {
.callback = lv_tick_cb,
.name = "lv_tick"
};
esp_timer_create(&args, &timer);
esp_timer_start_periodic(timer, 1000);
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[ILI9341_WIDTH * 20];
static lv_color_t buf2[ILI9341_WIDTH * 20];
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, ILI9341_WIDTH * 20);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = ILI9341_WIDTH;
disp_drv.ver_res = ILI9341_HEIGHT;
disp_drv.flush_cb = ili9341_flush_cb;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touch_cb;
lv_indev_drv_register(&indev_drv);
ui_create();
xTaskCreate(d25_button_task, "btn", 2048, NULL, 5, NULL);
esp_task_wdt_add(NULL);
while (1) {
esp_task_wdt_reset();
lv_timer_handler();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
7、最后加上LED
如果想做的再好一点,就可以加一个闪烁的led。比如选用d23连接gpio led就行。这种情况下就没必要ai编程了,手动加一下就好。
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_task_wdt.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "lvgl.h"
//================================================
// ILI9341 CONFIG
//================================================
#define ILI9341_SPI_HOST SPI2_HOST
#define ILI9341_SCLK_PIN 14
#define ILI9341_MOSI_PIN 13
#define ILI9341_CS_PIN 15
#define ILI9341_DC_PIN 2
#define ILI9341_RST_PIN 4
#define ILI9341_BL_PIN 12
#define ILI9341_WIDTH 320
#define ILI9341_HEIGHT 240
//================================================
// XPT2046 TOUCH CONFIG
//================================================
#define XPT2046_SPI_HOST SPI3_HOST
#define XPT2046_SCLK_PIN 18
#define XPT2046_MOSI_PIN 19
#define XPT2046_MISO_PIN 21
#define XPT2046_CS_PIN 5
#define XPT2046_IRQ_PIN 22
#define XPT2046_CMD_X 0xD0
#define XPT2046_CMD_Y 0x90
#define TOUCH_THRESHOLD 50
//================================================
// D25 / D26 / D23 CONFIG
//================================================
#define D25_BUTTON_PIN 25
#define D26_BUZZER_PIN 26
#define D23_LED_PIN 23
#define DEBOUNCE_MS 50
//================================================
// GLOBAL
//================================================
static spi_device_handle_t ili9341_spi;
static spi_device_handle_t xpt2046_spi;
static SemaphoreHandle_t lvgl_mutex;
static lv_obj_t *label_count;
static lv_obj_t *btn_increment;
static int counter = 0;
static bool d25_button_last_state = true;
static TickType_t d25_button_last_tick = 0;
//================================================
// LCD LOW LEVEL
//================================================
static void ili9341_send_cmd(uint8_t cmd)
{
gpio_set_level(ILI9341_DC_PIN, 0);
spi_transaction_t t = {
.length = 8,
.tx_buffer = &cmd
};
spi_device_transmit(ili9341_spi, &t);
}
static void ili9341_send_data(const uint8_t *data, size_t len)
{
gpio_set_level(ILI9341_DC_PIN, 1);
spi_transaction_t t = {
.length = len * 8,
.tx_buffer = data
};
spi_device_transmit(ili9341_spi, &t);
}
static void ili9341_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
ili9341_send_cmd(0x2A);
uint8_t col[] = {x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF};
ili9341_send_data(col, 4);
ili9341_send_cmd(0x2B);
uint8_t row[] = {y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF};
ili9341_send_data(row, 4);
ili9341_send_cmd(0x2C);
}
static void ili9341_flush_cb(lv_disp_drv_t *drv,
const lv_area_t *area,
lv_color_t *color_map)
{
uint32_t w = area->x2 - area->x1 + 1;
uint32_t h = area->y2 - area->y1 + 1;
ili9341_set_window(area->x1, area->y1, area->x2, area->y2);
gpio_set_level(ILI9341_DC_PIN, 1);
spi_transaction_t t = {
.length = w * h * 16,
.tx_buffer = color_map
};
spi_device_transmit(ili9341_spi, &t);
lv_disp_flush_ready(drv);
}
//================================================
// LCD INIT
//================================================
static void ili9341_init(void)
{
gpio_set_level(ILI9341_RST_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
gpio_set_level(ILI9341_RST_PIN, 1);
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x11);
vTaskDelay(pdMS_TO_TICKS(120));
ili9341_send_cmd(0x3A);
ili9341_send_data((uint8_t[]){0x55}, 1);
ili9341_send_cmd(0x36);
ili9341_send_data((uint8_t[]){0x28}, 1);
ili9341_send_cmd(0x29);
gpio_set_level(ILI9341_BL_PIN, 1);
}
//================================================
// SPI LCD INIT
//================================================
static void ili9341_spi_init(void)
{
gpio_config_t io = {
.pin_bit_mask =
(1ULL << ILI9341_DC_PIN) |
(1ULL << ILI9341_RST_PIN) |
(1ULL << ILI9341_BL_PIN),
.mode = GPIO_MODE_OUTPUT
};
gpio_config(&io);
spi_bus_config_t buscfg = {
.mosi_io_num = ILI9341_MOSI_PIN,
.miso_io_num = -1,
.sclk_io_num = ILI9341_SCLK_PIN,
.max_transfer_sz = ILI9341_WIDTH * ILI9341_HEIGHT * 2
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 20 * 1000 * 1000,
.mode = 0,
.spics_io_num = ILI9341_CS_PIN,
.queue_size = 7
};
spi_bus_initialize(ILI9341_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_bus_add_device(ILI9341_SPI_HOST, &devcfg, &ili9341_spi);
}
//================================================
// TOUCH INIT
//================================================
static void xpt2046_spi_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << XPT2046_IRQ_PIN),
.mode = GPIO_MODE_INPUT
};
gpio_config(&io);
spi_bus_config_t buscfg = {
.mosi_io_num = XPT2046_MOSI_PIN,
.miso_io_num = XPT2046_MISO_PIN,
.sclk_io_num = XPT2046_SCLK_PIN,
.max_transfer_sz = 8
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 2 * 1000 * 1000,
.mode = 0,
.spics_io_num = XPT2046_CS_PIN,
.queue_size = 1
};
spi_bus_initialize(XPT2046_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
spi_bus_add_device(XPT2046_SPI_HOST, &devcfg, &xpt2046_spi);
}
//================================================
// BUZZER INIT (D26)
//================================================
static void buzzer_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << D26_BUZZER_PIN) | (1ULL << D23_LED_PIN),
.mode = GPIO_MODE_OUTPUT
};
gpio_config(&io);
gpio_set_level(D26_BUZZER_PIN, 1);
gpio_set_level(D23_LED_PIN, 0);
}
//================================================
// D25 BUTTON
//================================================
static void d25_button_init(void)
{
gpio_config_t io = {
.pin_bit_mask = (1ULL << D25_BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE
};
gpio_config(&io);
d25_button_last_state = gpio_get_level(D25_BUTTON_PIN);
d25_button_last_tick = xTaskGetTickCount();
}
//================================================
// TOUCH
//================================================
static uint16_t xpt2046_read_adc(uint8_t cmd)
{
uint8_t tx[3] = {cmd, 0, 0};
uint8_t rx[3] = {0};
spi_transaction_t t = {
.length = 24,
.tx_buffer = tx,
.rx_buffer = rx
};
spi_device_transmit(xpt2046_spi, &t);
return ((rx[1] << 8) | rx[2]) >> 3;
}
static bool xpt2046_read_touch(uint16_t *x, uint16_t *y)
{
if (gpio_get_level(XPT2046_IRQ_PIN)) return false;
uint16_t raw_x = xpt2046_read_adc(XPT2046_CMD_X);
uint16_t raw_y = xpt2046_read_adc(XPT2046_CMD_Y);
*x = raw_x * ILI9341_WIDTH / 4095;
*y = raw_y * ILI9341_HEIGHT / 4095;
return true;
}
//================================================
// LVGL TOUCH
//================================================
static void touch_cb(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
uint16_t x, y;
if (xpt2046_read_touch(&x, &y)) {
data->state = LV_INDEV_STATE_PRESSED;
data->point.x = x;
data->point.y = y;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
//================================================
// UI
//================================================
static void btn_event_cb(lv_event_t *e)
{
if (lv_event_get_code(e) == LV_EVENT_CLICKED) {
if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY)) {
counter++;
char buf[32];
sprintf(buf, "Count: %d", counter);
lv_label_set_text(label_count, buf);
xSemaphoreGive(lvgl_mutex);
}
}
}
//================================================
// UI
//================================================
static void ui_create(void)
{
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), 0);
label_count = lv_label_create(lv_scr_act());
lv_label_set_text(label_count, "Count: 0");
lv_obj_align(label_count, LV_ALIGN_CENTER, 0, -40);
btn_increment = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn_increment, 120, 50);
lv_obj_align(btn_increment, LV_ALIGN_CENTER, 0, 40);
lv_obj_t *lbl = lv_label_create(btn_increment);
lv_label_set_text(lbl, "Press");
lv_obj_center(lbl);
// Register button click event
lv_obj_add_event_cb(btn_increment, btn_event_cb, LV_EVENT_CLICKED, NULL);
}
//================================================
// LVGL TICK
//================================================
static void lv_tick_cb(void *arg)
{
lv_tick_inc(1);
}
//================================================
// D25 TASK + BUZZER CONTROL
//================================================
static void d25_button_task(void *arg)
{
bool last_state = 1;
while (1) {
bool cur = gpio_get_level(D25_BUTTON_PIN);
if (last_state == 1 && cur == 0) {
gpio_set_level(D26_BUZZER_PIN, 0);
gpio_set_level(D23_LED_PIN, 1);
printf("PRESS\n");
if (xSemaphoreTake(lvgl_mutex, portMAX_DELAY)) {
counter++;
char buf[32];
sprintf(buf, "Count: %d", counter);
lv_label_set_text(label_count, buf);
xSemaphoreGive(lvgl_mutex);
}
}
if (last_state == 0 && cur == 1) {
gpio_set_level(D26_BUZZER_PIN, 1);
gpio_set_level(D23_LED_PIN, 0);
printf("RELEASE\n");
}
last_state = cur;
vTaskDelay(pdMS_TO_TICKS(10));
}
}
//================================================
// MAIN
//================================================
void app_main(void)
{
printf("START\n");
lvgl_mutex = xSemaphoreCreateMutex();
ili9341_spi_init();
ili9341_init();
xpt2046_spi_init();
d25_button_init();
buzzer_init(); // <<< 新增
lv_init();
esp_timer_handle_t timer;
esp_timer_create_args_t args = {
.callback = lv_tick_cb,
.name = "lv_tick"
};
esp_timer_create(&args, &timer);
esp_timer_start_periodic(timer, 1000);
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[ILI9341_WIDTH * 20];
static lv_color_t buf2[ILI9341_WIDTH * 20];
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, ILI9341_WIDTH * 20);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = ILI9341_WIDTH;
disp_drv.ver_res = ILI9341_HEIGHT;
disp_drv.flush_cb = ili9341_flush_cb;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touch_cb;
lv_indev_drv_register(&indev_drv);
ui_create();
xTaskCreate(d25_button_task, "btn", 2048, NULL, 5, NULL);
esp_task_wdt_add(NULL);
while (1) {
esp_task_wdt_reset();
lv_timer_handler();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
312

被折叠的 条评论
为什么被折叠?



