ESP32开发——SPI驱动水墨屏

这篇具有很好参考价值的文章主要介绍了ESP32开发——SPI驱动水墨屏。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

怎么说呢,感觉自己之前都白学了,又从头到尾看了一遍。

主要参考厂家给的源码,不过只有STM32的程序,但是大差不差,拿过来改一下就可以了,其次就是仔细查看芯片手册。

好的,最大的收获就是学会了如何翻手册,有问题翻手册!!

想要让水墨屏显示起来,需要利用spi来进行驱动,spi用来发送命令和数据,本质上来说还是设置寄存器。

发送的命令是地址,数据是要设置的值,这跟I2C其实没什么差别。

具体的什么spi的原理,其他文章都说的很全。

我也懒得写....

好,下面就记录一下我做的过程吧。

在做之前,先来看一下需要几个引脚,以及这些引脚都用来干啥。

查手册,以及我们开发板的硬件原理图。

ESP32开发——SPI驱动水墨屏

 首先这四根线是我们必须要有的。

ESP32开发——SPI驱动水墨屏

 ESP32开发——SPI驱动水墨屏

还有选择是命令/数据,复位以及忙线等。

所以综合来说,我们需要七根线。

MISO、MOSI、CLK、CS、D/C、RES、BUSY。

有了这些,就可以对我们的GPIO进行设置了。

需要进行GPIO初始化的只有后面四根线,别问我怎么知道,我翻的例程里面就是这么写的。

其实对于我们主控芯片来说,BUSY是输入,CS、D/C、RES都是输出。

其中BUSY存在中断,因为在选模式的时候,选的是SPI0的模式,上升沿采样、下降沿移出,所以此时设置BUSY下下降沿的时候触发中断,允许数据变化(这里理解好像有一点点问题)。

下面上代码:

void ds_screen_gpio_init(){
    gpio_config_t io_conf;
    //disable interrupt
    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = SCREEN_GPIO_OUTPUT_CS_SEL;
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 0;
    //configure GPIO with the given settings
    gpio_config(&io_conf);//初始化片选

    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = SCREEN_GPIO_OUTPUT_DC_SEL;
    //configure GPIO with the given settings
    gpio_config(&io_conf);//初始化D/C

    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = SCREEN_GPIO_OUTPUT_RES_SEL;
    //configure GPIO with the given settings
    gpio_config(&io_conf);//复位

    io_conf.intr_type = GPIO_INTR_NEGEDGE;//这里为啥要用下降沿来进行中断触发,存疑
    //bit mask of the pins, use GPIO4/5 here
    io_conf.pin_bit_mask = SCREEN_GPIO_INTPUT_BUSY_SEL;
    //set as input mode    
    io_conf.mode = GPIO_MODE_INPUT;
    //enable pull-up mode
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);
   
}

本部分参考官方STM32代码,如下图所示。

ESP32开发——SPI驱动水墨屏

GPIO基本就设置完了。

下面编写SPI的代码。

同样是参考官方例程。

首先初始化:

void screen_spi_init(void)
{
    esp_err_t ret;
    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);
    
}

这没啥好说的,直接参考手册就可以了。

ESP32开发——SPI驱动水墨屏

ESP32开发——SPI驱动水墨屏

初始化之后就是编写发送命令和发送数据的函数了,为了后面编写屏幕驱动做准备。

定义一个spi句柄。

spi_device_handle_t spi;

定义一个传输的结构体,并进行初始化。

spi_transaction_t t;
memset(&t, 0, sizeof(t)); 
struct spi_transaction_t {
    uint32_t flags;                 ///< Bitwise OR of SPI_TRANS_* flags
    uint16_t cmd;                   /**< Command data, of which the length is set in the ``command_bits`` of spi_device_interface_config_t.
                                      *
                                      *  <b>NOTE: this field, used to be "command" in ESP-IDF 2.1 and before, is re-written to be used in a new way in ESP-IDF 3.0.</b>
                                      *
                                      *  Example: write 0x0123 and command_bits=12 to send command 0x12, 0x3_ (in previous version, you may have to write 0x3_12).
                                      */
    uint64_t addr;                  /**< Address data, of which the length is set in the ``address_bits`` of spi_device_interface_config_t.
                                      *
                                      *  <b>NOTE: this field, used to be "address" in ESP-IDF 2.1 and before, is re-written to be used in a new way in ESP-IDF3.0.</b>
                                      *
                                      *  Example: write 0x123400 and address_bits=24 to send address of 0x12, 0x34, 0x00 (in previous version, you may have to write 0x12340000).
                                      */
    size_t length;                  ///< Total data length, in bits
    size_t rxlength;                ///< Total data length received, should be not greater than ``length`` in full-duplex mode (0 defaults this to the value of ``length``).
    void *user;                     ///< User-defined variable. Can be used to store eg transaction ID.
    union {
        const void *tx_buffer;      ///< Pointer to transmit buffer, or NULL for no MOSI phase
        uint8_t tx_data[4];         ///< If SPI_TRANS_USE_TXDATA is set, data set here is sent directly from this variable.
    };
    union {
        void *rx_buffer;            ///< Pointer to receive buffer, or NULL for no MISO phase. Written by 4 bytes-unit if DMA is used.
        uint8_t rx_data[4];         ///< If SPI_TRANS_USE_RXDATA is set, data is received directly to this variable
    };
} ;        //the rx data should start from a 32-bit aligned address to get around dma issue.

结构体的定义如上代码所示。

由此,编写我们的代码。

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.
}

到这里SPI的代码基本就写完了。

虽然说起来比较简单,但是其实花时间去看,还是会要点时间的。

最后就是编写驱动了。

到了这里,我们需要去翻手册,和移植代码。

首先看驱动一个屏幕的流程图。

ESP32开发——SPI驱动水墨屏

 这样其实就很好理解了。

然后再去看给的源码。

ESP32开发——SPI驱动水墨屏

这是初始化。

观察一下,发现基本是按照流程图来编写的。

打开芯片手册,我们需要知道这些地址都是些什么,设置了什么,代表什么意思,以便根据我们的需求进行修改。

ESP32开发——SPI驱动水墨屏

 这一段很好理解,复位。

ESP32开发——SPI驱动水墨屏

 将地址翻到12,发现仍然是复位。

ESP32开发——SPI驱动水墨屏

 将芯片手册翻到01和11这两个地址。ESP32开发——SPI驱动水墨屏

ESP32开发——SPI驱动水墨屏

发现01都是默认值,我们默认即可。

而11设置的是x方向递增,y方向递减。

这个得记住 ,因为这在后面编写驱动程序的时候非常重要,因为这就意味着我们的y_start是从高地址开始的,而x_start是从低地址开始的。

如果不清楚这个的话,很容易出现图像倒过来的现象。

后面的命令就以此类推,一个个去查询,弄懂是什么意思,自己需要什么就可以了。

代码如下:

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  改为//0x18 -->(24+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();
}

知道了这些,其他就很好办了。

参照STM32中的代码一个个编写。

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();   
}

其中,屏幕全刷很简单,比较麻烦的是局部刷,需要定位到某个区域。

所以这就跟我们之前设置的x和y地址递减递增的方向很有联系了,知道这个,再去编写局部刷新就简单很多了。

因为我用的屏幕是200*200的。

x方向是8位一个,所以X方向是0-24。

y方向是0-199。

所以算起来一共是25*200,共5000个数据。

这也是我们后面显示图像的数组。

局刷之所以麻烦,是因为每次局部刷都需要重新设置一下X和Y的起始地址和计数地址,其他没有什么。

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)//判断一下这里有没有超过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();
}

基本上就没有什么注意点了。

这里X和Y可以自己调节,根据自己的喜好来,只要图像能够正常显示就行。

OK,到这里基本上就结束了。

最后测试正常。

等之后做好自己的图片,取模显示出来,再放照片!!!


本篇参考ESP32技术参考手册、SSD1681芯片技术手册、ESP32API编写规范,水墨屏厂家STM32源码等...文章来源地址https://www.toymoban.com/news/detail-439365.html

到了这里,关于ESP32开发——SPI驱动水墨屏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 物联网开发笔记(89)- 使用Micropython开发ESP32开发板之合宙ESP32 C3开发板通过串口SPI控制st7789 TFT液晶屏1.3寸

    一、目的         这一节我们学习如何使用合宙的ESP32 C3开发板控制1.3寸彩色TFT显示屏模块,分辨率240*240,SPI接口,ST7789驱动芯片。 二、环境         ESP32  C3 + Thonny + 1.3寸 st7789液晶屏模块 + 几根杜邦线 + Win10 接线方法:   三、st7789 TFT显示屏驱动 st7789py.py   四、点亮

    2024年02月11日
    浏览(53)
  • 【STM32】BLDC驱动&控制开发笔记 | 07_SPI通信测试 - STM32F407用SPI配置DRV8323驱动芯片

    最近在埋头搞STM32 + 无刷直流电机控制,想实现用自己的STM32F407VGT6芯片板子,外加一块驱动板(目前选用到TI的DRV8302或者DRV8323驱动芯片),搞定电机驱动,最后实现比较好的控制效果。如果不是同一块芯片的同学也不用急着走,大体上都是可借鉴哒~ 本文主要实现使用SPI通信

    2024年02月08日
    浏览(55)
  • ESP32开发---驱动触摸屏

    采用 I2C 驱动触摸屏。 I2C 多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。 它有两条线,一条是 SCL (串行时钟总线),另外一条是SDA(串行数据线),这两条数据需要接上拉电阻,总线空闲的时候SCL和SDA处于高电

    2024年02月08日
    浏览(57)
  • STM32C0开发(1)----SPI 驱动WS2812灯珠

    本文介绍了如何使用STM32微控制器,结合STM32CubeMX配置工具和SPI通讯接口,来驱动和控制WS2812 LED灯带。这是一个集硬件连接、软件配置和编程开发于一体的综合性项目,目标是实现对LED灯带颜色和亮度的精确控制。 最近在弄ST的课程,需要样片的可以加群申请:615061293 。 ht

    2024年02月04日
    浏览(49)
  • 物联网开发笔记(91)- 使用Micropython开发ESP32开发板之通过串口SPI控制ILI9341 液晶屏显示文字

    一、目的         这一节我们学习如何使用ESP32开发板,通过串口SPI控制ILI9341 液晶屏。我使用的是3.2寸的,分辨率240x320 V1.0,TFT屏幕。文末有购买地址。 二、环境         ESP32(固件:esp32-20220618-v1.19.1.bin) + Thonny(V4.0.1) + ILI9341 液晶屏模块 + 几根杜邦线 + Win10 64位商业版

    2024年02月16日
    浏览(52)
  • 物联网开发笔记(96)- Micropython ESP32开发之SPI接口控制Micro SD卡TF卡模块挂载内存卡

    一、目的         这一节我们学习如何使用乐鑫的ESP32开发板连接SD卡模块,进行目录、文件的相关操作。         在早前我们也介绍过TFT SD卡的操作,这里我们重新复习一下。 物联网开发笔记(60)- 使用Micropython开发ESP32开发板之SPI接口控制Micro SD卡TF卡模块_microsd卡

    2024年01月25日
    浏览(48)
  • STM32H5开发(6)----SPI驱动TFT-LCD屏

    在嵌入式领域,TFT-LCD屏是最常用的显示解决方案之一,因为它们提供了丰富的颜色和高分辨率的图像显示能力。STM32H5作为ST的高性能微控制器系列,具备了强大的处理能力和多种通信接口,非常适合于驱动TFT-LCD显示屏,该液晶屏st7796或者ILI9488驱动芯片,这两个屏幕都是兼容

    2024年02月05日
    浏览(43)
  • 快速入门ESP32—— platformIO添加开源库和自己的开发库

    相关文章 快速入门ESP32——开发环境配置Arduino IDE 快速入门ESP32——开发环境配置PlatformIO IDE  很多人刚开始接触到platformIO的时候,可以还不知道怎么去利用好网上开源的库用到自己的工程上,还有就是不知道怎么添加自己的.h文件,按照之前stm32的思路是行不通的。本篇文章

    2024年02月03日
    浏览(53)
  • ESP32 Arduino (八) IIc和SPI

    ESP32有两个I2C控制器(也称为端口),负责处理两条I2C总线上的通信。每个I2C控制器都可以作为主机或从机运行。引脚21 默认的SDA, 引脚22是默认的SCL IIC需要引入自带库 Wire.h Wire继承steam类 steam类有的他都有。 1. 初始化IIC (以主机身份) :begin 2. 以主机身份像从机请求数据:

    2024年02月01日
    浏览(37)
  • STM32与ESP32的硬件SPI通信(个人学习记录)

            STM32F103ZET6作为主机,使用SPI2,ESP32S2作为从机,进行SPI双向通信;硬件接线如下:                                                 主机                        从机 CS                                           PB12 ——————  14 MOSI         

    2024年02月07日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包