【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
很多mcu都会都有一个小的屏幕,用于数据的显示。但是这些屏幕不可能很大,因为mcu的处理能力是有限的。并且,很多时候也不会用rgb屏幕,那样会占用很多的pin脚,这样spi就成为一个很好的选择。而且,spi不仅可以用来操作屏幕,还可以用来访问tf卡、可以用作ad和da转换芯片,这都是可以的,所以今天就来看看esp32如何开发spi屏幕的。

1、弄清spi屏幕的驱动芯片
常用的驱动芯片,都可以从网上找到的。我们一般拿到spi屏幕之后,先看一下驱动芯片是啥,比如我们这次用的芯片是st7789。知道了这个,后续的ai编程就有方向了。
2、了解spi屏幕的引脚
一般来说,spi屏幕的引脚会稍微复杂点。通常来说,有这么8个引脚。首先是电源和地,电源3.3v,或者是5v,这个由实际情况决定。其次是3个spi引脚,即cs、clk、mosi,因为屏幕没有输出,所以只有3个spi引脚。剩下来还有3个,一个是reset复位,一个是c/d转换,一个是blk背光。
3、分配好esp32的引脚
spi的引脚,一般都是由指定的pin约定好的,比如这里我们用了15、14、13,分别接cs、clk、mosi。剩下来的3个,其实随便找3个gpio即可,这边挑选了4、2、12。
4、连接好杜邦线
本身spi屏幕的引脚就是裸露在外面的,所以我们只需要用杜邦线直接连接即可。注意连接的时候,选用那种母对母的杜邦线。
5、开始ai编程
引脚连接没有问题之后,就可以考虑ai编程。这个时候,我们得告诉ai,当前是用esp32对st7789进行编程、spi接口、分辨率是240*240、使用的spi是SPI2_HOST、对应的reset/cmd/bl口是哪几个、准备显示什么界面等等,这些都告诉ai之后,一般就可以得到一份基础的spi屏幕驱动代码。
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
// ST7789 Configuration
#define ST7789_SPI_HOST SPI2_HOST
#define ST7789_SCLK_PIN 14
#define ST7789_MOSI_PIN 13
#define ST7789_CS_PIN 15
#define ST7789_DC_PIN 2
#define ST7789_RST_PIN 4
#define ST7789_BL_PIN 12
#define ST7789_WIDTH 240
#define ST7789_HEIGHT 240
// Commands
#define ST7789_SWRESET 0x01
#define ST7789_SLPOUT 0x11
#define ST7789_NORON 0x13
#define ST7789_INVON 0x21
#define ST7789_DISPON 0x29
#define ST7789_CASET 0x2A
#define ST7789_RASET 0x2B
#define ST7789_RAMWR 0x2C
#define ST7789_COLMOD 0x3A
#define ST7789_MADCTL 0x36
spi_device_handle_t st7789_spi;
static void st7789_send_cmd(uint8_t cmd)
{
gpio_set_level(ST7789_DC_PIN, 0);
spi_transaction_t t = {0};
t.length = 8;
t.tx_buffer = &cmd;
spi_device_transmit(st7789_spi, &t);
}
static void st7789_send_data(const uint8_t *data, size_t len)
{
gpio_set_level(ST7789_DC_PIN, 1);
spi_transaction_t t = {0};
t.length = len * 8;
t.tx_buffer = data;
spi_device_transmit(st7789_spi, &t);
}
static void st7789_send_byte(uint8_t data)
{
gpio_set_level(ST7789_DC_PIN, 1);
spi_transaction_t t = {0};
t.length = 8;
t.tx_buffer = &data;
spi_device_transmit(st7789_spi, &t);
}
void st7789_backlight_on(void)
{
gpio_set_level(ST7789_BL_PIN, 1);
}
void st7789_init(void)
{
gpio_set_level(ST7789_RST_PIN, 0);
vTaskDelay(100 / portTICK_PERIOD_MS);
gpio_set_level(ST7789_RST_PIN, 1);
vTaskDelay(100 / portTICK_PERIOD_MS);
st7789_send_cmd(ST7789_SWRESET);
vTaskDelay(150 / portTICK_PERIOD_MS);
st7789_send_cmd(ST7789_SLPOUT);
vTaskDelay(150 / portTICK_PERIOD_MS);
// Color mode 16-bit
st7789_send_cmd(ST7789_COLMOD);
st7789_send_byte(0x55);
vTaskDelay(10 / portTICK_PERIOD_MS);
// Memory access control
st7789_send_cmd(ST7789_MADCTL);
st7789_send_byte(0x08); // BGR mode
vTaskDelay(10 / portTICK_PERIOD_MS);
// Inversion on
st7789_send_cmd(ST7789_INVON);
vTaskDelay(10 / portTICK_PERIOD_MS);
// Normal display on
st7789_send_cmd(ST7789_NORON);
vTaskDelay(10 / portTICK_PERIOD_MS);
// Display on
st7789_send_cmd(ST7789_DISPON);
vTaskDelay(100 / portTICK_PERIOD_MS);
st7789_backlight_on();
}
void st7789_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
st7789_send_cmd(ST7789_CASET);
uint8_t ca[4] = {x0 >> 8, x0 & 0xFF, x1 >> 8, x1 & 0xFF};
st7789_send_data(ca, 4);
st7789_send_cmd(ST7789_RASET);
uint8_t ra[4] = {y0 >> 8, y0 & 0xFF, y1 >> 8, y1 & 0xFF};
st7789_send_data(ra, 4);
st7789_send_cmd(ST7789_RAMWR);
}
void st7789_fill(uint16_t color)
{
st7789_set_window(0, 0, ST7789_WIDTH-1, ST7789_HEIGHT-1);
uint8_t data[2] = {color >> 8, color & 0xFF};
for (int i = 0; i < ST7789_WIDTH * ST7789_HEIGHT; i++) {
st7789_send_data(data, 2);
}
}
void st7789_spi_init(void)
{
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << ST7789_CS_PIN) | (1ULL << ST7789_DC_PIN) |
(1ULL << ST7789_RST_PIN) | (1ULL << ST7789_BL_PIN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = 0,
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
spi_bus_config_t buscfg = {
.miso_io_num = -1,
.mosi_io_num = ST7789_MOSI_PIN,
.sclk_io_num = ST7789_SCLK_PIN,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = ST7789_WIDTH * ST7789_HEIGHT * 2,
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 10000000,
.mode = 0,
.spics_io_num = ST7789_CS_PIN,
.queue_size = 7,
};
spi_bus_initialize(ST7789_SPI_HOST, &buscfg, SPI_DMA_DISABLED);
spi_bus_add_device(ST7789_SPI_HOST, &devcfg, &st7789_spi);
}
void app_main(void)
{
printf("ST7789 Demo\n");
st7789_spi_init();
st7789_init();
// Color test
uint16_t colors[] = {0xFFFF, 0xF800, 0x07E0, 0x001F, 0x0000}; // White, Red, Green, Blue, Black
char *names[] = {"White", "Red", "Green", "Blue", "Black"};
while (1) {
for (int i = 0; i < 5; i++) {
printf("Color: %s\n", names[i]);
st7789_fill(colors[i]);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
}
6、编译和测试
**生成后的代码,首先保证能编译、能烧入。**如果烧入之后,没有画面,也不用着急,先看看是不是硬件问题、线有没有连好、排针有有错、电源是否上电等等。不是硬件问题的话,就可以告诉ai,目前遇到了什么样的情况。实际沟通的时候,直接描述情况即可,尽量言简意赅。
当然,生成的代码,也有可能一半是对的,一半是错的。比如,有可能显示位置不对,有可能颜色不对,有可能颜色没有交替改变。我们要做的,就是如实沟通、慢慢交互,只要硬件没问题,最终肯定可以得到自己想要的出图效果。