spi_device_handle_t lcd_handle;//同理,我把spi总线句柄设置成了全局变量
uint16_t LCD_height = 240;
uint16_t LCD_width = 320;
//下面抄的官方示例(显示方向有改动)
DRAM_ATTR static const lcd_init_cmd_t st_init_cmds[] = {
/* Memory Data Access Control, MX=MV=1, MY=ML=MH=0, RGB=0 */
{0x36, {0xa8}, 1},
/* Interface Pixel Format, 16bits/pixel for RGB/MCU interface */
{0x3A, {0x55}, 1},
/* Porch Setting */
{0xB2, {0x0c, 0x0c, 0x00, 0x33, 0x33}, 5},
/* Gate Control, Vgh=13.65V, Vgl=-10.43V */
{0xB7, {0x45}, 1},
/* VCOM Setting, VCOM=1.175V */
{0xBB, {0x2B}, 1},
/* LCM Control, XOR: BGR, MX, MH */
{0xC0, {0x2C}, 1},
/* VDV and VRH Command Enable, enable=1 */
{0xC2, {0x01, 0xff}, 2},
/* VRH Set, Vap=4.4+... */
{0xC3, {0x11}, 1},
/* VDV Set, VDV=0 */
{0xC4, {0x20}, 1},
/* Frame Rate Control, 60Hz, inversion=0 */
{0xC6, {0x0f}, 1},
/* Power Control 1, AVDD=6.8V, AVCL=-4.8V, VDDS=2.3V */
{0xD0, {0xA4, 0xA1}, 1},
/* Positive Voltage Gamma Control */
{0xE0, {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19}, 14},
/* Negative Voltage Gamma Control */
{0xE1, {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19}, 14},
/* Sleep Out */
{0x11, {0}, 0x80},
/* Display On */
{0x29, {0}, 0x80},
{0, {0}, 0xff}
};
//这里我理解的是发送前会调用这个函数
void lcd_pre_callback(spi_transaction_t *t)
{
int dc = (int)t->user;
gpio_set_level(LCD_DC_IO_NUM, dc);
}
void lcd_init()
{
/*spi初始化 */
//spi总线配置结构体
spi_bus_config_t spi_cfg = {
.sclk_io_num = GPIO_NUM_12,
.mosi_io_num = GPIO_NUM_11,
.flags = SPICOMMON_BUSFLAG_MASTER,
.miso_io_num = -1,
.quadhd_io_num = -1,
.quadwp_io_num = -1,
};
//lcd设备配置结构体
spi_device_interface_config_t lcd_cfg = {
.mode = 0,
.clock_speed_hz = 10000000,
.queue_size = 8,
.spics_io_num = GPIO_NUM_10,
.pre_cb = lcd_pre_callback,
};
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &spi_cfg, SPI_DMA_CH_AUTO));
ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &lcd_cfg, &lcd_handle));
//rst dc bl 引脚初始化
gpio_config_t lcd_io_cfg = {
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pin_bit_mask = (1ull << LCD_DC_IO_NUM) | (1ull << LCD_BL_IO_NUM) | (1ull << LCD_RST_IO_NUM),
};
gpio_config(&lcd_io_cfg);
//硬件复位
gpio_set_level(LCD_RST_IO_NUM, 0);
vTaskDelay(100 / portTICK_PERIOD_MS);
gpio_set_level(LCD_RST_IO_NUM, 1);
vTaskDelay(100 / portTICK_PERIOD_MS);
//初始化
int cmd = 0;
while (st_init_cmds[cmd].databytes != 0xff) {
lcd_cmd(st_init_cmds[cmd].cmd, false);
lcd_data(st_init_cmds[cmd].data, st_init_cmds[cmd].databytes & 0x1F);
if (st_init_cmds[cmd].databytes & 0x80) {
vTaskDelay(100 / portTICK_PERIOD_MS);
}
cmd++;
}
///Enable backlight
gpio_set_level(LCD_BL_IO_NUM, 1);
}
3. 发送命令和数据的函数:抄的官方示例
void lcd_cmd(const uint8_t cmd, bool keep_cs_active)
{
esp_err_t ret;
spi_transaction_t t;
memset(&t, 0, sizeof(t)); //Zero out the transaction
t.length = 8; //Command is 8 bits
t.tx_buffer = &cmd; //The data is the cmd itself
t.user = LCD_CMD_MODE; //D/C needs to be set to 0
if (keep_cs_active) {
t.flags = SPI_TRANS_CS_KEEP_ACTIVE; //Keep CS active after data transfer
}
ret = spi_device_polling_transmit(lcd_handle, &t); //Transmit!
assert(ret == ESP_OK); //Should have had no issues.
}
void lcd_data(const uint8_t *data, int len)
{
spi_transaction_t t;
if (len == 0) {
return; //no need to send anything
}
memset(&t, 0, sizeof(t)); //Zero out the transaction
t.length = len * 8; //Len is in bytes, transaction length is in bits.
t.tx_buffer = data; //Data
t.user = LCD_DATA_MODE; //D/C needs to be set to 1
ESP_ERROR_CHECK(spi_device_polling_transmit(lcd_handle, &t));
}
4. 设置显示区域:借鉴了官方示例,没有使用lcd_cmd和lcd_data函数,那样太慢了
void lcd_set_window(uint16_t startX, uint16_t endX, uint16_t startY, uint16_t endY)
{
spi_transaction_t trans[4];
for (uint8_t i = 0; i < 4; i++)
{
memset(&trans[i], 0, sizeof(spi_transaction_t));
if ((i & 1) == 0)
{
trans[i].length = 8;
trans[i].user = LCD_CMD_MODE;
}
else
{
trans[i].length = 8 * 4;
trans[i].user = LCD_DATA_MODE;
}
trans[i].flags = SPI_TRANS_USE_TXDATA;
}
trans[0].tx_data[0] = 0x2a;
trans[1].tx_data[0] = startX >> 8;
trans[1].tx_data[1] = startX& 0xff;
trans[1].tx_data[2] = endX >> 8;
trans[1].tx_data[3] = endX & 0xff;
trans[2].tx_data[0] = 0x2b;
trans[3].tx_data[0] = startY >> 8;
trans[3].tx_data[1] = startY & 0xff;
trans[3].tx_data[2] = endY >> 8;
trans[3].tx_data[3] = endY& 0xff;
for (uint8_t i = 0; i < 4; i++)
{
ESP_ERROR_CHECK(spi_device_queue_trans(lcd_handle, &trans[i], portMAX_DELAY));
}
}
5. 画点,没什么好说的:
void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color)
{
uint8_t buf[2];
buf[0] = color >> 8;
buf[1] = color;
lcd_set_window(x, x, y, y);
lcd_cmd(0x2c, false);
lcd_data(buf, 2);
}
6. 清屏:扫描线的方式填充,使用画点函数太慢
void lcd_clear(uint16_t color)
{
uint8_t *buf;
buf = heap_caps_malloc(320 * sizeof(uint16_t), MALLOC_CAP_DMA);
memset(buf, color, 320 * sizeof(uint16_t));
lcd_set_window(0, LCD_width-1, 0, LCD_height-1);
lcd_cmd(0x2c, false);
for (uint16_t i = 0; i < LCD_height; i++)
{
lcd_data(buf, 320 * 2);
}
heap_caps_free(buf);
}
7. 显示字符:ascii16是字模,这是从我很久以前用stm32写lcd的时候写的函数,字模忘记怎么取得了。。。。。这部分挺好改的。
void LCD_ShowChar(uint16_t x, uint16_t y, uint8_t ch, uint8_t size,
uint16_t color, uint16_t bg_color, uint8_t mode)
{
uint8_t temp;
uint16_t y0 = y, x0 = x;
uint8_t idx = 0;
for (uint8_t i = 0; i < size/2; i++)
{
for (uint8_t t = 0; t < size/8; t++)
{
switch (size)
{
case 16:
temp = ascii16[ch-32][idx];
break;
case 24:
temp = ascii24[ch-32][idx];
break;
case 32:
temp = ascii32[ch-32][idx];
break;
default:
break;
}
idx++;
for (uint8_t j = 0; j < 8; j++)
{
if (temp & 0x80)
lcd_draw_point(x0, y0, color);
else
if (mode == 1)
{
lcd_draw_point(x0, y0, bg_color);
}
y0++;
temp <<= 1;
}
}
y0 = y;
x0++;
}
}
8. 显示字符串:循环调用显示字符函数就完了
void LCD_ShowString(uint16_t x, uint16_t y, char *str, uint8_t size,
uint16_t color, uint16_t bg_color, uint8_t mode)
{
uint8_t idx = 0;
while (str[idx] != '\0')
{
LCD_ShowChar(x, y, str[idx], size, color, bg_color, mode);
x+=size/2;
idx++;
}
}
三、主函数
void app_main(void)
{
i2c_init();
sht40_init();
lcd_init();
float t_rh[2];
char t_rh_str[22];
vTaskDelay(100/portTICK_PERIOD_MS);
lcd_clear(LCD_COLOR_BLACK);
vTaskDelay(100/portTICK_PERIOD_MS);
while (1)
{
sht40_read_temp(0xfd, t_rh);
printf("temp:%.2f RH:%.2f\r\n", t_rh[0], t_rh[1]);
sprintf(t_rh_str, "T: %.2foC, RH: %.2f%c", t_rh[0], t_rh[1], '%');
LCD_ShowString(0, 0, t_rh_str, 32, LCD_COLOR_RED, LCD_COLOR_BLACK, 1);
vTaskDelay(100);
}
}
只显示温湿度的话这些函数就够了,但很明显,这就是垃圾程序。我先写成这样完成作业再说吧,以后我会慢慢完善的。而且这次硬件设计上完全可以做成小电视的,后续我会再学一下esp32的联网怎么搞和lvgl尝试把小电视整出来。还有就是完成工程源码先不放出来了,写的太烂了,做完整理完再上传吧。
(打工真的没时间啊!!!)
评论