ESP32开发——SPI驱动水墨屏

news/2024/10/19 14:51:08/

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

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

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

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

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

具体的什么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 interruptio_conf.intr_type = GPIO_PIN_INTR_DISABLE;//set as output modeio_conf.mode = GPIO_MODE_OUTPUT;//bit mask of the pins that you want to set,e.g.GPIO18/19io_conf.pin_bit_mask = SCREEN_GPIO_OUTPUT_CS_SEL;//disable pull-down modeio_conf.pull_down_en = 0;//disable pull-up modeio_conf.pull_up_en = 0;//configure GPIO with the given settingsgpio_config(&io_conf);//初始化片选//bit mask of the pins that you want to set,e.g.GPIO18/19io_conf.pin_bit_mask = SCREEN_GPIO_OUTPUT_DC_SEL;//configure GPIO with the given settingsgpio_config(&io_conf);//初始化D/C//bit mask of the pins that you want to set,e.g.GPIO18/19io_conf.pin_bit_mask = SCREEN_GPIO_OUTPUT_RES_SEL;//configure GPIO with the given settingsgpio_config(&io_conf);//复位io_conf.intr_type = GPIO_INTR_NEGEDGE;//这里为啥要用下降沿来进行中断触发,存疑//bit mask of the pins, use GPIO4/5 hereio_conf.pin_bit_mask = SCREEN_GPIO_INTPUT_BUSY_SEL;//set as input mode    io_conf.mode = GPIO_MODE_INPUT;//enable pull-up modeio_conf.pull_up_en = 1;gpio_config(&io_conf);}

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

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 busret=spi_bus_initialize(HSPI_HOST, &buscfg, 0);ESP_ERROR_CHECK(ret);//Attach the LCD to the SPI busret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);ESP_ERROR_CHECK(ret);}

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

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

定义一个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_* flagsuint16_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 bitssize_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 phaseuint8_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 bitst.tx_buffer=&cmd;               //The data is the cmd itselft.user=(void*)0;                //D/C needs to be set to 0ret=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 transactiont.length=8;                 //Len is in bytes, transaction length is in bits.t.tx_buffer=&data;               //Datat.user=(void*)1;                //D/C needs to be set to 1ret=spi_device_polling_transmit(spi, &t);  //Transmit!ds_gpio_set_screen_cs(1);assert(ret==ESP_OK);            //Should have had no issues.
}

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

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

最后就是编写驱动了。

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

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

 这样其实就很好理解了。

然后再去看给的源码。

这是初始化。

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

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

 这一段很好理解,复位。

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

 将芯片手册翻到01和11这两个地址。

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

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

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

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

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

代码如下:

static void init_display(){vTaskDelay(10 / portTICK_PERIOD_MS);ds_gpio_set_screen_rst(0);		// Module resetvTaskDelay(10 / portTICK_PERIOD_MS);ds_gpio_set_screen_rst(1);vTaskDelay(100 / portTICK_PERIOD_MS);lcd_chkstatus();   spi_send_cmd(0x12);  //SWRESETlcd_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 =200spi_send_cmd(0x45); //set Ram-Y address start/end position          spi_send_data(0xC7);    //0xC7-->(199+1)=200spi_send_data(0x00);spi_send_data(0x00);spi_send_data(0x00); spi_send_cmd(0x3C); //BorderWavefromspi_send_data(0x05);	spi_send_cmd(0x18); //Read built-in temperature sensorspi_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 sleepspi_send_data(0x01); vTaskDelay(100 / portTICK_PERIOD_MS);
}void refresh(void)
{spi_send_cmd(0x22); //Display Update Controlspi_send_data(0xF7);   spi_send_cmd(0x20); //Activate Display Update Sequencelcd_chkstatus();   
}void refresh_part(void)
{spi_send_cmd(0x22); //Display Update Controlspi_send_data(0xFF);   spi_send_cmd(0x20); //Activate Display Update Sequencelcd_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 changeds_gpio_set_screen_rst(0);		// Module resetvTaskDelay(10 / portTICK_PERIOD_MS);ds_gpio_set_screen_rst(1);vTaskDelay(10 / portTICK_PERIOD_MS);//Lock the border to prevent flashingspi_send_cmd(0x3C); //BorderWavefrom,spi_send_data(0x80);	spi_send_cmd(0x44);       // set RAM x address start/end, in page 35spi_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 35spi_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 RAMpartial_new();refresh_part();deep_sleep();
}

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

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

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

最后测试正常。

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


本篇参考ESP32技术参考手册、SSD1681芯片技术手册、ESP32API编写规范,水墨屏厂家STM32源码等...


http://www.ppmy.cn/news/884368.html

相关文章

14Finger-全能web指纹识别与分享平台

14Finger 功能齐全的Web指纹扫描和分享平台,基于vue3django前后端分离的web架构&#xff0c;并集成了长亭出品的rad爬虫的功能&#xff0c;内置了一万多条互联网开源的指纹信息。 Github&#xff1a;https://github.com/b1ackc4t/14Finger 特点 基于强大的rad爬虫分析指纹&a…

物联网开发106 - Micropython ESP32连接st7735s屏幕使用字库显示汉字

一、目的 这一节我们来学习如何使用ESP32连接st7735s 128*160的1.8寸LCD屏幕显示汉字&#xff0c;非取字模方式&#xff0c;使用字库用来显示汉字。前面章节我们也介绍过字库的生成方法。请看这个文章&#xff1a; 物联网开发100 - Micropython ESP32 S3非取字模和刷写中文固件…

html5查看ies文件,5千+ IES光域网文件 5312 IES Files + IES 预览

适用软件:所有 官方地址:DEMO 资源标签:光阈网,IES IES 标准文件&#xff0c;可以运用光度学数据的 IES LM-63-1991 标准文件格局&#xff0c;创立 IES 格局的光度学数据文件。 (IES 代表照明工程协会。) 但是&#xff0c;此处只介绍与 3ds max 有关的信息。 有关 IES 标准文件…

SpringMVC的pom.xml、xxx-servelt.xml、web.xml

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.…

ASO优化之在海外如何进行竞品分析

进行竞争对手研究&#xff0c;需要分析当前的市场形势&#xff0c;确定竞争对手表现的模式&#xff0c;并将其应用到我们应用营销策略中。不同的应用可以在直观的用户界面上进行简单的比较&#xff0c;很容易看到长期趋势。 在App Store和Google Play上获取竞争对手评论区的信…

(转自)何新:谈“玄”(一)

如果我们承认真理是不可知的&#xff0c;那我们也就无法理解&#xff1a;为什么我们还要浪费精神而来研究哲学&#xff1f; ——黑格尔① ①有时我故意不提供引文出处&#xff0c;考考那些通人和“专家”吧。幽他一默。 &#xff3b;本文原只是答一位朋友的信。但改来改去…

mina下载

mina下载地址&#xff1a;https://mina.apache.org/downloads-mina.html 目前mina最新版本是2.0.13 解压后的mina文件夹如下&#xff1a; 文件夹具体作用如下&#xff1a; dist文件夹&#xff1a;mina库代码运行的jar包 docs文件夹&#xff1a;api文档 lib文件夹&#xff1a;使…

国王杯:瓦伦西亚3:0晋级八强 米纳梅开二度

资料图&#xff1a;瓦伦西亚球员在比赛中。 中新社发 韩海丹 摄 中新网1月16日电 2018-19赛季西班牙国王杯1/8决赛次回合展开争夺&#xff0c;瓦伦西亚主场3&#xff1a;0完胜希洪竞技&#xff0c;以总分4&#xff1a;2翻盘晋级八强。 首回合比赛&#xff0c;瓦伦西亚1&#…