Signed-off-by: kevin <kevin@lmve.net>

This commit is contained in:
2026-03-18 02:03:00 +08:00
parent c113005780
commit 6d43d12308
7 changed files with 358 additions and 27 deletions
+2 -2
View File
@@ -3,8 +3,8 @@
"idf.openOcdConfigs": [ "idf.openOcdConfigs": [
"board/esp32s3-builtin.cfg" "board/esp32s3-builtin.cfg"
], ],
"idf.portWin": "COM9", "idf.portWin": "COM10",
"idf.currentSetup": "C:\\Users\\wuwen\\esp\\v5.5.2\\esp-idf", "idf.currentSetup": "C:\\esp\\v5.5.3\\esp-idf",
"idf.customExtraVars": { "idf.customExtraVars": {
"OPENOCD_SCRIPTS": "C:\\Espressif\\tools\\openocd-esp32\\v0.11.0-esp32-20220411/openocd-esp32/share/openocd/scripts", "OPENOCD_SCRIPTS": "C:\\Espressif\\tools\\openocd-esp32\\v0.11.0-esp32-20220411/openocd-esp32/share/openocd/scripts",
"IDF_CCACHE_ENABLE": "1", "IDF_CCACHE_ENABLE": "1",
+289 -17
View File
@@ -2,34 +2,306 @@
static const char *TAG = "E-PAPER"; static const char *TAG = "E-PAPER";
void epd_gpio_init(void) /********************* 全局变量 *********************/
uint8_t epd_buffer[EPD_BUFFER_SIZE] = {0}; // 显存缓冲区
// ==================== 底层硬件操作函数 ====================
/**
* @brief 设置引脚电平
*/
static void epd_set_pin_level(gpio_num_t pin, uint8_t level)
{ {
// 1. 配置CS/DC为输出模式 gpio_set_level(pin, level);
gpio_config_t io_conf = {0}; }
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << EPD_CS_PIN) | (1ULL << EPD_DC_PIN); /**
io_conf.pull_up_en = GPIO_PULLUP_DISABLE; * @brief 等待busy引脚释放(低电平忙,高电平空闲)
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; */
io_conf.intr_type = GPIO_INTR_DISABLE; static void epd_wait_busy(void)
{
uint32_t busy_cnt = 0;
ESP_LOGD(TAG, "waiting epd busy release...");
while (gpio_get_level(EPD_BUSY_PIN) == 0) {
vTaskDelay(pdMS_TO_TICKS(10));
busy_cnt++;
// 超时保护(最大等待1000ms)
if (busy_cnt > 100) {
ESP_LOGE(TAG, "epd busy timeout!");
break;
}
}
}
/**
* @brief 初始化gpio引脚
*/
static esp_err_t epd_gpio_init(void)
{
// 配置输出引脚:cs/dc/rst
gpio_config_t io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = (1ULL << EPD_CS_PIN) | (1ULL << EPD_DC_PIN),
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&io_conf); gpio_config(&io_conf);
// 2. 配置BUSY为输入模式(上拉,防止电平飘移 // 配置输入引脚:busy(上拉
io_conf.mode = GPIO_MODE_INPUT; io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = (1ULL << EPD_BUSY_PIN); io_conf.pin_bit_mask = (1ULL << EPD_BUSY_PIN);
io_conf.pull_up_en = GPIO_PULLUP_ENABLE; io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_config(&io_conf); gpio_config(&io_conf);
// 3. 初始化引脚默认电平 // 初始化默认电平
gpio_set_level(EPD_CS_PIN, 1); // CS默认拉高(未选中) epd_set_pin_level(EPD_CS_PIN, 1);
gpio_set_level(EPD_DC_PIN, 0); // DC默认拉低(指令模式) epd_set_pin_level(EPD_DC_PIN, 0);
ESP_LOGI(TAG, "EPD GPIO init success (CS:%d, DC:%d)", return ESP_OK;
EPD_CS_PIN, EPD_DC_PIN);
} }
void epd_init() /**
* @brief spi发送单字节
*/
static esp_err_t epd_spi_send_byte(uint8_t data, uint8_t is_data)
{
// 控制dc引脚(0=指令,1=数据)
epd_set_pin_level(EPD_DC_PIN, is_data);
vTaskDelay(pdMS_TO_TICKS(1));
// 拉低cs选中设备
epd_set_pin_level(EPD_CS_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(1));
// 同步传输
esp_err_t ret = epd_spi_send_sync_fullduplex(&data,NULL,1);
// 释放cs
epd_set_pin_level(EPD_CS_PIN, 1);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "spi send byte failed: %s", esp_err_to_name(ret));
}
return ret;
}
/**
* @brief 发送指令
*/
static esp_err_t epd_send_cmd(uint8_t cmd)
{
return epd_spi_send_byte(cmd, 0); // 0=指令模式
}
/**
* @brief 发送数据
*/
static esp_err_t epd_send_data(uint8_t data)
{
return epd_spi_send_byte(data, 1); // 1=数据模式
}
/**
* @brief 批量发送数据
*/
static esp_err_t epd_send_data_bulk(const uint8_t *data, size_t len)
{
if (len == 0 || data == NULL) {
return ESP_ERR_INVALID_ARG;
}
epd_set_pin_level(EPD_DC_PIN, 1);
epd_set_pin_level(EPD_CS_PIN, 0);
vTaskDelay(pdMS_TO_TICKS(1));
esp_err_t ret = epd_spi_send_sync_fullduplex(data,NULL,len);
epd_set_pin_level(EPD_CS_PIN, 1);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "spi send bulk failed: %s", esp_err_to_name(ret));
}
return ret;
}
/**
* @brief 硬件复位
*/
static void epd_hw_reset(void)
{
// epd_set_pin_level(EPD_RST_PIN, 1);
// vTaskDelay(pdMS_TO_TICKS(200));
// epd_set_pin_level(EPD_RST_PIN, 0);
// vTaskDelay(pdMS_TO_TICKS(200));
// epd_set_pin_level(EPD_RST_PIN, 1);
// vTaskDelay(pdMS_TO_TICKS(200));
}
/**
* @brief 墨水屏软件复位(指令模拟)
*/
void epd_soft_reset(void)
{
ESP_LOGI(TAG, "epd soft reset start");
// 1. 关闭电源,等待忙信号释放
epd_send_cmd(POWER_OFF);
epd_wait_busy();
// 2. 进入深度睡眠,清空内部状态
epd_send_cmd(DEEP_SLEEP);
epd_send_data(0xa5); // 睡眠指令必须跟0xa5
vTaskDelay(pdMS_TO_TICKS(100)); // 等待睡眠完成
ESP_LOGI(TAG, "epd soft reset done");
}
esp_err_t epd_init()
{ {
ESP_LOGI(TAG, "init_GPIO"); ESP_LOGI(TAG, "init_GPIO");
epd_gpio_init(); esp_err_t ret = epd_gpio_init();
if (ret != ESP_OK) return ret;
// 2. 硬件复位
epd_hw_reset();
epd_send_cmd(POWER_ON);
vTaskDelay(pdMS_TO_TICKS(200));
//epd_soft_reset();
epd_wait_busy();
// 3. 发送初始化指令
epd_send_cmd(POWER_SETTING);
epd_send_data(0x03);
epd_send_data(0x00);
epd_send_data(0x2b);
epd_send_data(0x2b);
epd_send_data(0x09);
epd_send_cmd(BOOSTER_SOFT_START);
epd_send_data(0x07);
epd_send_data(0x07);
epd_send_data(0x17);
epd_send_cmd(POWER_ON);
epd_wait_busy();
epd_send_cmd(PANEL_SETTING);
epd_send_data(0xbf); // 黑白模式
epd_send_data(0x0d);
epd_send_cmd(PLL_CONTROL);
epd_send_data(0x3c);
epd_send_cmd(VCOM_AND_DATA_INTERVAL_SETTING);
epd_send_data(0x77);
epd_send_cmd(TCON_RESOLUTION);
epd_send_data(EPD_WIDTH >> 8);
epd_send_data(EPD_WIDTH & 0xff);
epd_send_data(EPD_HEIGHT >> 8);
epd_send_data(EPD_HEIGHT & 0xff);
epd_send_cmd(VCM_DC_SETTING_REGISTER);
epd_send_data(0x12);
ESP_LOGI(TAG, "epd init success");
return ESP_OK;
}
/**
* @brief 清屏(color: 0=黑,1=白)
*/
esp_err_t epd_clear(uint8_t color)
{
uint8_t fill_data = color ? 0xff : 0x00;
memset(epd_buffer, fill_data, EPD_BUFFER_SIZE);
// 发送清屏数据
epd_send_cmd(DATA_START_TRANSMISSION_1);
ESP_LOGI(TAG, "epd clear");
vTaskDelay(pdMS_TO_TICKS(2));
epd_send_data_bulk(epd_buffer, EPD_BUFFER_SIZE);
ESP_LOGI(TAG, "epd clear sended");
vTaskDelay(pdMS_TO_TICKS(2));
// 刷新屏幕
epd_send_cmd(DISPLAY_REFRESH);
epd_wait_busy();
return ESP_OK;
}
/**
* @brief 画点(x:0~249, y:0~121, color:0=黑,1=白)
*/
void epd_draw_point(uint16_t x, uint16_t y, uint8_t color)
{
if (x >= EPD_WIDTH || y >= EPD_HEIGHT) {
ESP_LOGW(TAG, "point out of range: x=%d, y=%d", x, y);
return;
}
uint32_t index = (x / 8) + y * (EPD_WIDTH / 8);
uint8_t bit = 7 - (x % 8);
if (color) {
epd_buffer[index] |= (1 << bit); // 白色置1
} else {
epd_buffer[index] &= ~(1 << bit); // 黑色置0
}
}
/**
* @brief 刷新缓冲区到屏幕
*/
esp_err_t epd_refresh(void)
{
epd_send_cmd(DATA_START_TRANSMISSION_1);
vTaskDelay(pdMS_TO_TICKS(2));
epd_send_data_bulk(epd_buffer, EPD_BUFFER_SIZE);
vTaskDelay(pdMS_TO_TICKS(2));
epd_send_cmd(DISPLAY_REFRESH);
epd_wait_busy();
ESP_LOGI(TAG, "epd refresh done");
return ESP_OK;
}
/**
* @brief 进入深度睡眠模式(低功耗)
*/
void epd_sleep(void)
{
epd_send_cmd(POWER_OFF);
epd_wait_busy();
epd_send_cmd(DEEP_SLEEP);
epd_send_data(0xa5); // 睡眠指令必须跟0xa5
ESP_LOGI(TAG, "epd enter sleep mode");
}
/**
* @brief 获取epd缓冲区指针
*/
uint8_t *epd_get_buffer(void)
{
return epd_buffer;
} }
+46 -1
View File
@@ -14,7 +14,52 @@
#define EPD_DC_PIN 35 #define EPD_DC_PIN 35
#define EPD_BUSY_PIN 37 #define EPD_BUSY_PIN 37
/********************* 屏幕参数定义 *********************/
#define EPD_WIDTH 320 // 屏幕宽度
#define EPD_HEIGHT 240 // 屏幕高度
#define EPD_BUFFER_SIZE ((EPD_WIDTH * EPD_HEIGHT) / 8) // 显存大小(1bit/像素)
void epd_init(); /********************* 指令集定义 *********************/
#define PANEL_SETTING 0x00
#define POWER_SETTING 0x01
#define POWER_OFF 0x02
#define POWER_OFF_SEQUENCE_SETTING 0x03
#define POWER_ON 0x04
#define POWER_ON_MEASURE 0x05
#define BOOSTER_SOFT_START 0x06
#define DEEP_SLEEP 0x07
#define DATA_START_TRANSMISSION_1 0x10
#define DATA_STOP 0x11
#define DISPLAY_REFRESH 0x12
#define DATA_START_TRANSMISSION_2 0x13
#define PLL_CONTROL 0x30
#define TEMPERATURE_SENSOR_COMMAND 0x40
#define TEMPERATURE_SENSOR_CALIBRATION 0x41
#define TEMPERATURE_SENSOR_WRITE 0x42
#define TEMPERATURE_SENSOR_READ 0x43
#define VCOM_AND_DATA_INTERVAL_SETTING 0x50
#define LOW_POWER_DETECTION 0x51
#define TCON_SETTING 0x60
#define TCON_RESOLUTION 0x61
#define SOURCE_AND_GATE_START_SETTING 0x62
#define GET_STATUS 0x71
#define AUTO_MEASURE_VCOM 0x80
#define READ_VCOM_VALUE 0x81
#define VCM_DC_SETTING_REGISTER 0x82
#define PARTIAL_WINDOW 0x90
#define PARTIAL_IN 0x91
#define PARTIAL_OUT 0x92
#define PROGRAM_MODE 0xA0
#define ACTIVE_PROGRAM 0xA1
#define READ_OTP_DATA 0xA2
#define POWER_SAVING 0xE3
esp_err_t epd_init();
esp_err_t epd_clear(uint8_t color);
void epd_draw_point(uint16_t x, uint16_t y, uint8_t color);
esp_err_t epd_refresh(void);
void epd_sleep(void);
uint8_t *epd_get_buffer(void);
#endif #endif
+11 -2
View File
@@ -59,16 +59,25 @@ void app_main(void)
size_t internal_free = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); size_t internal_free = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
size_t internal_total = heap_caps_get_total_size(MALLOC_CAP_INTERNAL); size_t internal_total = heap_caps_get_total_size(MALLOC_CAP_INTERNAL);
ESP_LOGI(TAG,"内部 RAM 总大小:%u KB,可用:%u KB\n", internal_total/1024, internal_free/1024); ESP_LOGI(TAG,"内部 RAM 总大小:%u KB,可用:%u KB", internal_total/1024, internal_free/1024);
// PSRAM 信息 // PSRAM 信息
size_t psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); size_t psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
size_t psram_total = heap_caps_get_total_size(MALLOC_CAP_SPIRAM); size_t psram_total = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
ESP_LOGI(TAG,"PSRAM 总大小:%u KB,可用:%u KB\n", psram_total/1024, psram_free/1024); ESP_LOGI(TAG,"PSRAM 总大小:%u KB,可用:%u KB", psram_total/1024, psram_free/1024);
spi_init(); spi_init();
epd_init(); epd_init();
epd_clear(1);
// 3. 画测试点(黑色)
epd_draw_point(50, 50, 0); // (50,50) 黑点
epd_draw_point(50, 60, 0); // (50,60) 黑点
epd_draw_point(60, 50, 0); // (60,50) 黑点
epd_draw_point(60, 60, 0); // (60,60) 黑点
// 4. 刷新屏幕
epd_refresh();
// 1. 初始化 SPIFFS // 1. 初始化 SPIFFS
+4 -4
View File
@@ -18,13 +18,13 @@ void spi_init()
.sclk_io_num = VSPI_SCLK, // 时钟引脚 .sclk_io_num = VSPI_SCLK, // 时钟引脚
.quadwp_io_num = -1, // 不使用 QWP .quadwp_io_num = -1, // 不使用 QWP
.quadhd_io_num = -1, // 不使用 QHD .quadhd_io_num = -1, // 不使用 QHD
.max_transfer_sz = 4096, // 最大传输大小 .max_transfer_sz = 9600, // 最大传输大小
// .flags = 0, // .flags = 0,
// .intr_flags = 0, // .intr_flags = 0,
}; };
// 2. 初始化 SPI 总线 // 2. 初始化 SPI 总线
ret = spi_bus_initialize(USE_SPI_HOST, &buscfg, SPI_DMA_DISABLED); ret = spi_bus_initialize(USE_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
if (ret != ESP_OK) if (ret != ESP_OK)
{ {
ESP_LOGE(TAG, "IO总线初始化失败"); ESP_LOGE(TAG, "IO总线初始化失败");
@@ -40,7 +40,7 @@ void spi_init()
.address_bits = 0, // 无地址位 .address_bits = 0, // 无地址位
.dummy_bits = 0, .dummy_bits = 0,
.mode = 0, // SPI 模式 0 .mode = 0, // SPI 模式 0
.clock_speed_hz = 80000000, // .clock_speed_hz = 4000000, //
.spics_io_num = -1, .spics_io_num = -1,
.queue_size = 1, // 队列深度,因为有几个设备共用了这个SPI,而有的设备有busy信号,准备整个专门的线程去控制如果有busy信号先跳过这次传输 .queue_size = 1, // 队列深度,因为有几个设备共用了这个SPI,而有的设备有busy信号,准备整个专门的线程去控制如果有busy信号先跳过这次传输
.flags = SPI_DEVICE_NO_DUMMY, // 禁用 dummy 周期 .flags = SPI_DEVICE_NO_DUMMY, // 禁用 dummy 周期
@@ -90,7 +90,7 @@ esp_err_t epd_spi_send_sync_fullduplex(const uint8_t *tx_data, uint8_t *rx_data,
.length = len * 8, // 传输位数(1字节=8位,收发长度一致) .length = len * 8, // 传输位数(1字节=8位,收发长度一致)
.tx_buffer = tx_data, // 发送缓冲区(NULL则发送0x00 .tx_buffer = tx_data, // 发送缓冲区(NULL则发送0x00
.rx_buffer = rx_data, // 接收缓冲区(NULL则丢弃接收数据) .rx_buffer = rx_data, // 接收缓冲区(NULL则丢弃接收数据)
.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA, // 强制使用本地缓冲区 //.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA, // 强制使用本地缓冲区
// 全双工关键:无需额外flag,只要同时配置tx/rx_buffer即启用全双工 // 全双工关键:无需额外flag,只要同时配置tx/rx_buffer即启用全双工
}; };
+1
View File
@@ -15,6 +15,7 @@
void spi_init(); void spi_init();
esp_err_t epd_spi_send_sync_fullduplex(const uint8_t *tx_data, uint8_t *rx_data, size_t len);
extern spi_device_handle_t spi2; extern spi_device_handle_t spi2;
+5 -1
View File
@@ -1,6 +1,6 @@
# #
# Automatically generated file. DO NOT EDIT. # Automatically generated file. DO NOT EDIT.
# Espressif IoT Development Framework (ESP-IDF) 5.5.2 Project Configuration # Espressif IoT Development Framework (ESP-IDF) 5.5.3 Project Configuration
# #
CONFIG_SOC_ADC_SUPPORTED=y CONFIG_SOC_ADC_SUPPORTED=y
CONFIG_SOC_UART_SUPPORTED=y CONFIG_SOC_UART_SUPPORTED=y
@@ -370,6 +370,7 @@ CONFIG_SOC_WIFI_HW_TSF=y
CONFIG_SOC_WIFI_FTM_SUPPORT=y CONFIG_SOC_WIFI_FTM_SUPPORT=y
CONFIG_SOC_WIFI_GCMP_SUPPORT=y CONFIG_SOC_WIFI_GCMP_SUPPORT=y
CONFIG_SOC_WIFI_WAPI_SUPPORT=y CONFIG_SOC_WIFI_WAPI_SUPPORT=y
CONFIG_SOC_WIFI_TXOP_SUPPORT=y
CONFIG_SOC_WIFI_CSI_SUPPORT=y CONFIG_SOC_WIFI_CSI_SUPPORT=y
CONFIG_SOC_WIFI_MESH_SUPPORT=y CONFIG_SOC_WIFI_MESH_SUPPORT=y
CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y
@@ -697,6 +698,7 @@ CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y
# #
# Hardware Settings # Hardware Settings
# #
CONFIG_ESP_HW_SUPPORT_FUNC_IN_IRAM=y
# #
# Chip revision # Chip revision
@@ -757,6 +759,8 @@ CONFIG_RTC_CLK_SRC_INT_RC=y
# CONFIG_RTC_CLK_SRC_EXT_OSC is not set # CONFIG_RTC_CLK_SRC_EXT_OSC is not set
# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set # CONFIG_RTC_CLK_SRC_INT_8MD256 is not set
CONFIG_RTC_CLK_CAL_CYCLES=1024 CONFIG_RTC_CLK_CAL_CYCLES=1024
CONFIG_RTC_CLK_FUNC_IN_IRAM=y
CONFIG_RTC_TIME_FUNC_IN_IRAM=y
# end of RTC Clock Config # end of RTC Clock Config
# #