桌面小屏幕实战课程:DesktopScreen 11 SPI 水墨屏

发布于:2025-06-27 ⋅ 阅读:(19) ⋅ 点赞:(0)

飞书文档https://x509p6c8to.feishu.cn/docx/doxcnlzpIgj3gosCZufBTCZxlMb

SPI说明

SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上占用四根线。

参考源码位置

/home/kemp/work/esp/esp-idf/examples/peripherals/spi_master

源码下载方式参考:

源码下载方式

屏幕接口

SCLK IO25 SPI 串口通信时钟信号线。

SDI  IO26 SPI 串口通信数据信号线。

CS  IO27 片选,低电平有效。

D/C IO14 数据/命令 读写选择,高电平为数据,低电平为命令。

RES IO12 电子纸复位信号,低电平有效。

BUSY IO13 电子纸刷新时,BUSY 引脚发出忙信号给主 MCU,此时 MCU 无法对电子纸驱动 IC 进行读写操作;电子纸刷新完成后,BUSY 引脚发出闲置状态信号,此时 MCU 可以对 电子纸驱动 IC 进行读写操作。GDEW 系列电子纸 BUSY 引脚忙状态为高电平(GDEH 系列为低电平),BUSY 引脚空闲状态反之。

墨水屏原理

152*152个像素 19Byte=152Bit 19*152的数组

152*152    19BYTE   1BYTE=8BIT  0000 0000  1111 1111

更多屏幕相关手册

1.54寸电子纸屏/ 152x152分辨率电子纸/ 四灰阶/ 支持局部刷新电子墨水屏 GDEW0154T8D_电子纸屏-大连佳显电子有限公司

代码说明

SPI部分

SPI Master Driver - - ‒ ESP-IDF 编程指南 release-v4.1 文档

/* SPI Master example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"

#include "ds_gpio.h"

//#define PIN_NUM_MISO 1  //屏幕只写不读,此引脚不用
#define PIN_NUM_MOSI 26
#define PIN_NUM_CLK  25
#define PIN_NUM_CS   27

spi_device_handle_t spi;

void spi_send_cmd(const uint8_t cmd)
{
    esp_err_t ret;
    spi_transaction_t t;
    //设置发送指令
    ds_gpio_set_screen_dc(0);
    //设置片选模块
    ds_gpio_set_screen_cs(0);
    memset(&t, 0, sizeof(t));       //Zero out the transaction
    // t.flags=SPI_TRANS_USE_TXDATA;
    t.length=8;                     //Command is 8 bits
    t.tx_buffer=&cmd;               //The data is the cmd itself
    t.user=(void*)0;                //D/C needs to be set to 0
    //发送指令
    ret=spi_device_polling_transmit(spi, &t);  //Transmit!
    //取消片选模块
    ds_gpio_set_screen_cs(1);
    assert(ret==ESP_OK);            //Should have had no issues.
}

void spi_send_data(const uint8_t data)
{
    esp_err_t ret;
    spi_transaction_t t;
    //设置发送数据
    ds_gpio_set_screen_dc(1);
    //设置片选模块
    ds_gpio_set_screen_cs(0);
    memset(&t, 0, sizeof(t));       //Zero out the transaction
    t.length=8;                 //Len is in bytes, transaction length is in bits.
    t.tx_buffer=&data;               //Data
    t.user=(void*)1;                //D/C needs to be set to 1
    //发送数据
    ret=spi_device_polling_transmit(spi, &t);  //Transmit!
    //取消片选模块
    ds_gpio_set_screen_cs(1);
    assert(ret==ESP_OK);            //Should have had no issues.
}

//This function is called (in irq context!) just before a transmission starts. It will
//set the D/C line to the value indicated in the user field.
void spi_pre_transfer_callback(spi_transaction_t *t)
{
    int dc=(int)t->user;
    printf("dc callback\n");
    ds_gpio_set_screen_dc(dc);
}

void screen_spi_init(void)
{
    esp_err_t ret;
    //IO设置
    spi_bus_config_t buscfg={
        .miso_io_num = PIN_NUM_MISO,                // MISO信号线
        .mosi_io_num = PIN_NUM_MOSI,                // MOSI信号线
        .sclk_io_num = PIN_NUM_CLK,                 // SCLK信号线
        .quadwp_io_num = -1,                        // WP信号线,专用于QSPI的D2
        .quadhd_io_num = -1,                        // HD信号线,专用于QSPI的D3
        .max_transfer_sz = 64*8,                    // 最大传输数据大小

    };
    //速率 模式设置
    spi_device_interface_config_t devcfg={
        .clock_speed_hz=15*1000*1000,            //Clock out at 26 MHz
        .mode=0,                                //SPI mode 0
        .queue_size=7,                          //We want to be able to queue 7 transactions at a time
        // .pre_cb=spi_pre_transfer_callback,  //Specify pre-transfer callback to handle D/C line
    };
    //Initialize the SPI bus
    ret=spi_bus_initialize(HSPI_HOST, &buscfg, 0);
    ESP_ERROR_CHECK(ret);
    //Attach the LCD to the SPI bus
    ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
    ESP_ERROR_CHECK(ret);
   
}

void screen_spi_test(){
    spi_send_cmd(0x55);
    vTaskDelay(10 / portTICK_PERIOD_MS);
    spi_send_data(0x55);
}

屏幕驱动部分

注意:这里的代码和视频中不太一样,因为旧版本屏幕停产,更换了屏幕型号,下方代码为0154B-D67的屏幕驱动代码,和最新版本屏幕是一致的。

#include <string.h>
#include <stdio.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"

#include "ds_screen.h"
#include "ds_gpio.h"
#include "ds_spi.h"
#include "ds_data_image.h"

// Detection busy
static void lcd_chkstatus(void)
{
    int count = 0;
    unsigned char busy;
    while (1)
    {
        busy = ds_gpio_get_screen_busy();
        busy = (busy & 0x01);
        //=1 BUSY
        if (busy == 0)
            break;
        vTaskDelay(10 / portTICK_PERIOD_MS);
        count++;
        if (count >= 1000)
        {
            printf("---------------time out ---\n");
            break;
        }
    }
}

static void init_display()
{
    vTaskDelay(10 / portTICK_PERIOD_MS);
    ds_gpio_set_screen_rst(0); // Module reset
    vTaskDelay(10 / portTICK_PERIOD_MS);
    ds_gpio_set_screen_rst(1);
    vTaskDelay(100 / portTICK_PERIOD_MS);

    lcd_chkstatus();
    spi_send_cmd(0x12); // SWRESET
    lcd_chkstatus();

    spi_send_cmd(0x01); // Driver output control
    spi_send_data(0xC7);
    spi_send_data(0x00);
    spi_send_data(0x01);

    spi_send_cmd(0x11); // data entry mode
    spi_send_data(0x01);

    spi_send_cmd(0x44); // set Ram-X address start/end position
    spi_send_data(0x00);
    spi_send_data(0x18); // 0x0C-->(18+1)*8=200

    spi_send_cmd(0x45);  // set Ram-Y address start/end position
    spi_send_data(0xC7); // 0xC7-->(199+1)=200
    spi_send_data(0x00);
    spi_send_data(0x00);
    spi_send_data(0x00);

    spi_send_cmd(0x3C); // BorderWavefrom
    spi_send_data(0x05);

    spi_send_cmd(0x18); // Read built-in temperature sensor
    spi_send_data(0x80);

    spi_send_cmd(0x4E); // set RAM x address count to 0;
    spi_send_data(0x00);
    spi_send_cmd(0x4F); // set RAM y address count to 0X199;
    spi_send_data(0xC7);
    spi_send_data(0x00);

    vTaskDelay(100 / portTICK_PERIOD_MS);
    lcd_chkstatus();
}

/Enter deep sleep mode
void deep_sleep(void) // Enter deep sleep mode
{
    spi_send_cmd(0x10); // enter deep sleep
    spi_send_data(0x01);
    vTaskDelay(100 / portTICK_PERIOD_MS);
}

void refresh(void)
{
    spi_send_cmd(0x22); // Display Update Control
    spi_send_data(0xF7);
    spi_send_cmd(0x20); // Activate Display Update Sequence
    lcd_chkstatus();
}

void refresh_part(void)
{
    spi_send_cmd(0x22); // Display Update Control
    spi_send_data(0xFF);
    spi_send_cmd(0x20); // Activate Display Update Sequence
    lcd_chkstatus();
}

// 图片全刷-全白函数
static void ds_screen_display_white(void)
{
    unsigned int i, k;
    spi_send_cmd(0x24); // write RAM for black(0)/white (1)
    for (k = 0; k < 250; k++)
    {
        for (i = 0; i < 25; i++)
        {
            spi_send_data(0xff);
        }
    }
}

// 图片全刷-数据函数
void ds_screen_full_display_data(const uint8_t *data)
{

    unsigned int i;
    spi_send_cmd(0x24); // write RAM for black(0)/white (1)
    for (i = 0; i < 5000; i++)
    {
        spi_send_data(*data);
        data++;
    }
}

// 全刷 不带数据
void ds_screen_full_display(void pic_display(void))
{
    init_display();
    pic_display(); // picture
    refresh();     // EPD_refresh
    deep_sleep();
}

// 全刷 带数据
void ds_screen_full_display_bydata(void display_func(const uint8_t *data), const uint8_t *data)
{
    init_display();
    display_func(data); // picture
    refresh();          // EPD_refresh
    deep_sleep();
}

// 局部刷 不带数据
void ds_screen_partial_display(unsigned int x_start, unsigned int y_start, void partial_new(void), unsigned int PART_COLUMN, unsigned int PART_LINE)
{
    unsigned int i;
    unsigned int x_end, y_start1, y_start2, y_end1, y_end2;
    x_start = x_start / 8;
    x_end = x_start + PART_LINE / 8 - 1;

    y_start1 = 0;
    y_start2 = 200 - y_start;
    if (y_start >= 256)
    {
        y_start1 = y_start2 / 256;
        y_start2 = y_start2 % 256;
    }
    y_end1 = 0;
    y_end2 = y_start2 + PART_COLUMN - 1;
    if (y_end2 >= 256)
    {
        y_end1 = y_end2 / 256;
        y_end2 = y_end2 % 256;
    }

    // Add hardware reset to prevent background color change
    ds_gpio_set_screen_rst(0); // Module reset
    vTaskDelay(10 / portTICK_PERIOD_MS);
    ds_gpio_set_screen_rst(1);
    vTaskDelay(10 / portTICK_PERIOD_MS);
    // Lock the border to prevent flashing
    spi_send_cmd(0x3C); // BorderWavefrom,
    spi_send_data(0x80);

    spi_send_cmd(0x44);      // set RAM x address start/end, in page 35
    spi_send_data(x_start);  // RAM x address start at 00h;
    spi_send_data(x_end);    // RAM x address end at 0fh(15+1)*8->128
    spi_send_cmd(0x45);      // set RAM y address start/end, in page 35
    spi_send_data(y_start2); // RAM y address start at 0127h;
    spi_send_data(y_start1); // RAM y address start at 0127h;
    spi_send_data(y_end2);   // RAM y address end at 00h;
    spi_send_data(y_end1);   // ????=0

    spi_send_cmd(0x4E); // set RAM x address count to 0;
    spi_send_data(x_start);
    spi_send_cmd(0x4F); // set RAM y address count to 0X127;
    spi_send_data(y_start2);
    spi_send_data(y_start1);

    spi_send_cmd(0x24); // Write Black and White image to RAM
    partial_new();

    refresh_part();
    deep_sleep();
}

// 局部刷 带数据
void ds_screen_partial_display_bydata(unsigned int x_start, unsigned int y_start,
                                      void partial_new(const uint8_t *data), const uint8_t *new_data,
                                      unsigned int PART_COLUMN, unsigned int PART_LINE)
{
    printf("x_start=%d x_end=%d y_start=%d y_end=%d\n", x_start, y_start, PART_COLUMN, PART_LINE);

    unsigned int i;
    unsigned int x_end, y_start1, y_start2, y_end1, y_end2;
    x_start = x_start / 8;
    x_end = x_start + PART_LINE / 8 - 1;

    y_start1 = 0;
    y_start2 = 200 - y_start;
    if (y_start >= 256)
    {
        y_start1 = y_start2 / 256;
        y_start2 = y_start2 % 256;
    }
    y_end1 = 0;
    y_end2 = y_start2 + PART_COLUMN - 1;
    if (y_end2 >= 256)
    {
        y_end1 = y_end2 / 256;
        y_end2 = y_end2 % 256;
    }
    // Add hardware reset to prevent background color change
    ds_gpio_set_screen_rst(0); // Module reset
    vTaskDelay(10 / portTICK_PERIOD_MS);
    ds_gpio_set_screen_rst(1);
    vTaskDelay(10 / portTICK_PERIOD_MS);
    // Lock the border to prevent flashing
    spi_send_cmd(0x3C); // BorderWavefrom,
    spi_send_data(0x80);

    spi_send_cmd(0x44);      // set RAM x address start/end, in page 35
    spi_send_data(x_start);  // RAM x address start at 00h;
    spi_send_data(x_end);    // RAM x address end at 0fh(15+1)*8->128
    spi_send_cmd(0x45);      // set RAM y address start/end, in page 35
    spi_send_data(y_start2); // RAM y address start at 0127h;
    spi_send_data(y_start1); // RAM y address start at 0127h;
    spi_send_data(y_end2);   // RAM y address end at 00h;
    spi_send_data(y_end1);   // ????=0

    spi_send_cmd(0x4E); // set RAM x address count to 0;
    spi_send_data(x_start);
    spi_send_cmd(0x4F); // set RAM y address count to 0X127;
    spi_send_data(y_start2);
    spi_send_data(y_start1);

    spi_send_cmd(0x24); // Write Black and White image to RAM
    partial_new(new_data);
}

uint8_t partial_data[200][25];
uint8_t partial_data_array[5000];

void ds_screen_partial_data_init()
{
    for (int index = 0; index < 200; index++)
    {
        for (int yindex = 0; yindex < 25; yindex++)
        {
            partial_data[index][yindex] = 0xff;
        }
    }
}

void ds_screen_partial_data_add(unsigned int x_start, unsigned int x_end, unsigned int y_start, unsigned int y_end, const uint8_t *data)
{
    uint8_t x_len = x_end - x_start;
    // uint8_t y_len = y_end - y_start;
    uint8_t x_data_location = x_start / 8;
    uint8_t x_size = x_len / 8;
    int data_index = 0;
    // int move = x_start % 8;
    if (x_start % 8 != 0)
    {
        x_data_location++;
    }
    if (x_len % 8 != 0)
    {
        x_size++;
    }
    for (int x_index = y_start; x_index < y_end; x_index++)
    {
        for (int y_index = x_data_location; y_index < (x_data_location + x_size); y_index++)
        {
            partial_data[x_index][y_index] = (~data[data_index]);
            data_index++;
        }
    }
}

// 图片全刷-全白函数
static void ds_screen_display_data(void)
{
    unsigned int i;
    spi_send_cmd(0x24);
    for (i = 0; i < 5000; i++)
    {
        spi_send_data(partial_data_array[i]);
    }
    spi_send_cmd(0x26);
    for (i = 0; i < 5000; i++)
    {
        spi_send_data(partial_data_array[i]);
    }
}

// 局刷数据-复制
void ds_screen_partial_data_copy()
{
    int data_index = 0;
    for (int index = 0; index < 200; index++)
    {
        for (int yindex = 0; yindex < 25; yindex++)
        {
            partial_data_array[data_index] = partial_data[index][yindex];
            data_index++;
        }
    }
    ds_screen_full_display(ds_screen_display_data);
}

// 接口初始化
void init_screen_interface()
{
    ds_screen_gpio_init();
    screen_spi_init();
}

// 清屏为白色
void ds_screen_clean_white()
{
    ds_screen_init();
    vTaskDelay(2000 / portTICK_PERIOD_MS);
}

// 初始化
void ds_screen_init()
{
    ds_screen_full_display(ds_screen_display_white); // EPD_sleep
}

参考:

【科普贴】SPI接口详解_湉湉家的小虎子的博客-CSDN博客_spi

1.54寸高频刷新 快刷1.5秒电子墨水屏 分辨率200x200 SPI串口 GDEY0154D67_电子墨水屏幕-大连佳显电子


网站公告

今日签到

点亮在社区的每一天
去签到