diff --git a/code/hello_world_01/.vscode/settings.json b/code/hello_world_01/.vscode/settings.json index 226b5d8..b630d6f 100644 --- a/code/hello_world_01/.vscode/settings.json +++ b/code/hello_world_01/.vscode/settings.json @@ -3,8 +3,8 @@ "idf.openOcdConfigs": [ "board/esp32s3-builtin.cfg" ], - "idf.portWin": "COM9", - "idf.currentSetup": "C:\\Users\\wuwen\\esp\\v5.5.2\\esp-idf", + "idf.portWin": "COM10", + "idf.currentSetup": "C:\\esp\\v5.5.3\\esp-idf", "idf.customExtraVars": { "OPENOCD_SCRIPTS": "C:\\Espressif\\tools\\openocd-esp32\\v0.11.0-esp32-20220411/openocd-esp32/share/openocd/scripts", "IDF_CCACHE_ENABLE": "1", diff --git a/code/hello_world_01/main/epaper.c b/code/hello_world_01/main/epaper.c index 616ea76..219aa3a 100644 --- a/code/hello_world_01/main/epaper.c +++ b/code/hello_world_01/main/epaper.c @@ -2,34 +2,306 @@ 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_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; - io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; - io_conf.intr_type = GPIO_INTR_DISABLE; + gpio_set_level(pin, level); +} + +/** + * @brief 等待busy引脚释放(低电平忙,高电平空闲) + */ +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); - // 2. 配置BUSY为输入模式(上拉,防止电平飘移) + // 配置输入引脚:busy(上拉) io_conf.mode = GPIO_MODE_INPUT; io_conf.pin_bit_mask = (1ULL << EPD_BUSY_PIN); io_conf.pull_up_en = GPIO_PULLUP_ENABLE; - io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; gpio_config(&io_conf); - // 3. 初始化引脚默认电平 - gpio_set_level(EPD_CS_PIN, 1); // CS默认拉高(未选中) - gpio_set_level(EPD_DC_PIN, 0); // DC默认拉低(指令模式) + // 初始化默认电平 + epd_set_pin_level(EPD_CS_PIN, 1); + epd_set_pin_level(EPD_DC_PIN, 0); + - ESP_LOGI(TAG, "EPD GPIO init success (CS:%d, DC:%d)", - EPD_CS_PIN, EPD_DC_PIN); + return ESP_OK; } -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"); - 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; } \ No newline at end of file diff --git a/code/hello_world_01/main/epaper.h b/code/hello_world_01/main/epaper.h index d48b2d4..fe026f8 100644 --- a/code/hello_world_01/main/epaper.h +++ b/code/hello_world_01/main/epaper.h @@ -14,7 +14,52 @@ #define EPD_DC_PIN 35 #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 \ No newline at end of file diff --git a/code/hello_world_01/main/hello_world_main.c b/code/hello_world_01/main/hello_world_main.c index 02939b6..2bc93d9 100644 --- a/code/hello_world_01/main/hello_world_main.c +++ b/code/hello_world_01/main/hello_world_main.c @@ -59,16 +59,25 @@ void app_main(void) size_t internal_free = heap_caps_get_free_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 信息 size_t psram_free = heap_caps_get_free_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(); 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 diff --git a/code/hello_world_01/main/spi.c b/code/hello_world_01/main/spi.c index dcaa76d..9405652 100644 --- a/code/hello_world_01/main/spi.c +++ b/code/hello_world_01/main/spi.c @@ -18,13 +18,13 @@ void spi_init() .sclk_io_num = VSPI_SCLK, // 时钟引脚 .quadwp_io_num = -1, // 不使用 QWP .quadhd_io_num = -1, // 不使用 QHD - .max_transfer_sz = 4096, // 最大传输大小 + .max_transfer_sz = 9600, // 最大传输大小 // .flags = 0, // .intr_flags = 0, }; // 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) { ESP_LOGE(TAG, "IO总线初始化失败"); @@ -40,7 +40,7 @@ void spi_init() .address_bits = 0, // 无地址位 .dummy_bits = 0, .mode = 0, // SPI 模式 0 - .clock_speed_hz = 80000000, // + .clock_speed_hz = 4000000, // .spics_io_num = -1, .queue_size = 1, // 队列深度,因为有几个设备共用了这个SPI,而有的设备有busy信号,准备整个专门的线程去控制如果有busy信号先跳过这次传输 .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位,收发长度一致) .tx_buffer = tx_data, // 发送缓冲区(NULL则发送0x00) .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即启用全双工 }; diff --git a/code/hello_world_01/main/spi.h b/code/hello_world_01/main/spi.h index c6b8b26..8b0c452 100644 --- a/code/hello_world_01/main/spi.h +++ b/code/hello_world_01/main/spi.h @@ -15,6 +15,7 @@ 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; diff --git a/code/hello_world_01/sdkconfig b/code/hello_world_01/sdkconfig index 1adbe58..534b930 100644 --- a/code/hello_world_01/sdkconfig +++ b/code/hello_world_01/sdkconfig @@ -1,6 +1,6 @@ # # 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_UART_SUPPORTED=y @@ -370,6 +370,7 @@ CONFIG_SOC_WIFI_HW_TSF=y CONFIG_SOC_WIFI_FTM_SUPPORT=y CONFIG_SOC_WIFI_GCMP_SUPPORT=y CONFIG_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SOC_WIFI_TXOP_SUPPORT=y CONFIG_SOC_WIFI_CSI_SUPPORT=y CONFIG_SOC_WIFI_MESH_SUPPORT=y CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y @@ -697,6 +698,7 @@ CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y # # Hardware Settings # +CONFIG_ESP_HW_SUPPORT_FUNC_IN_IRAM=y # # 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_INT_8MD256 is not set 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 #