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

飞书文档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_电子墨水屏幕-大连佳显电子

相关推荐
未来之窗软件服务41 分钟前
智能 IOT 设备管理系统的设计与实现 —— 从管理及售后维护视角——毕业论文——东方仙盟
物联网·仙盟创梦ide·东方仙盟
nuannuan2311a3 小时前
4N90-ASEMI电机控制专用4N90
单片机
邹诗钰-电子信息工程3 小时前
bmp280的压力数据采集(i2c设备驱动+设备树编写)
stm32·单片机·嵌入式硬件
Ronin-Lotus7 小时前
嵌入式硬件篇---有线串口通信问题解决
单片机·嵌入式硬件·ttl·rs232·rs485·有线串口
GalaxySinCos9 小时前
08 51单片机之串口通信
单片机·嵌入式硬件·51单片机
悠哉悠哉愿意9 小时前
【电赛学习笔记】MaxiCAM 项目实践——与单片机的串口通信
笔记·python·单片机·嵌入式硬件·学习·视觉检测
李某学编程11 小时前
Cortex-M内核SysTick定时器介绍
stm32·单片机
李永奉11 小时前
STM32-定时器的基本定时/计数功能实现配置教程(寄存器版)
c语言·开发语言·stm32·单片机·嵌入式硬件
糖糖单片机设计12 小时前
硬件开发_基于STM32单片机的电脑底座系统
stm32·单片机·嵌入式硬件·物联网·51单片机
IT项目分享15 小时前
ESP32入门实战:PC远程控制LED灯完整指南
单片机·嵌入式硬件·micropython·it项目网