#include "lcd_tft_154.h" // 等待通讯完成 static void _wait_finish(spi_t *lcd_spi) { while ((lcd_spi->spi->Instance->SR & 0x0080) != RESET) ; // 等待通信完成 } // 等待发送缓冲区清空 static void _wait_send_empty(spi_t *lcd_spi) { while ((lcd_spi->spi->Instance->SR & 0x0002) == 0) ; // 等待发送缓冲区清空 } static void _write_command(spi_t *lcd_spi, uint8_t command) { _wait_finish(lcd_spi); // 等待通讯完成 lcd_spi->gpios.dc->reset(*lcd_spi->gpios.dc); // DC置低,表示发送命令 (lcd_spi->spi)->Instance->DR = command; // 发送数据 _wait_send_empty(lcd_spi); // 等待发送缓冲区清空 _wait_finish(lcd_spi); // 等待通讯完成 等待通信完成 lcd_spi->gpios.dc->set(*lcd_spi->gpios.dc); // DC置高,表示发送数据 } static void _write_data_8bit(spi_t *lcd_spi, uint8_t data) { lcd_spi->spi->Instance->DR = data; // 发送数据 _wait_send_empty(lcd_spi); // 等待发送缓冲区清空 } static void _write_data_16bit(spi_t *lcd_spi, uint16_t data) { lcd_spi->spi->Instance->DR = data >> 8; // 发送数据,高8位 _wait_send_empty(lcd_spi); // 等待发送缓冲区清空 lcd_spi->spi->Instance->DR = data; // 发送数据,低8位 _wait_send_empty(lcd_spi); // 等待发送缓冲区清空 } static void _write_buff(lcd_t *lcd, uint16_t *data, uint16_t size) { uint32_t i; lcd->info.spi->spi->Instance->CR1 &= 0xFFBF; // 关闭SPI lcd->info.spi->spi->Instance->CR1 |= 0x0800; // 切换成16位数据格式 lcd->info.spi->spi->Instance->CR1 |= 0x0040; // 使能SPI lcd->info.spi->gpios.cs->reset(*lcd->info.spi->gpios.cs); for (i = 0; i < size; i++) { lcd->info.spi->spi->Instance->DR = data[i]; _wait_send_empty(lcd->info.spi); // 等待发送缓冲区清空 } _wait_finish(lcd->info.spi); // 等待通讯完成 lcd->info.spi->gpios.cs->set(*lcd->info.spi->gpios.cs); lcd->info.spi->spi->Instance->CR1 &= 0xFFBF; // 关闭SPI lcd->info.spi->spi->Instance->CR1 &= 0xF7FF; // 切换成8位数据格式 lcd->info.spi->spi->Instance->CR1 |= 0x0040; // 使能SPI } // 设置方向 static void _set_dir(lcd_t *lcd, uint8_t scan_dir) { lcd->info.dir = scan_dir; lcd->info.spi->gpios.cs->reset(*lcd->info.spi->gpios.cs); if (scan_dir == Direction_H) // 横屏显示 { _write_command(lcd->info.spi, 0x36); // 显存访问控制 指令,用于设置访问显存的方式 _write_data_8bit(lcd->info.spi, 0x70); // 横屏显示 lcd->info.x_offset = 0; // 设置控制器坐标偏移量 lcd->info.y_offset = 0; } else if (scan_dir == Direction_V) { _write_command(lcd->info.spi, 0x36); // 显存访问控制 指令,用于设置访问显存的方式 _write_data_8bit(lcd->info.spi, 0x00); // 垂直显示 lcd->info.x_offset = 0; // 设置控制器坐标偏移量 lcd->info.y_offset = 0; } else if (scan_dir == Direction_H_Flip) { _write_command(lcd->info.spi, 0x36); // 显存访问控制 指令,用于设置访问显存的方式 _write_data_8bit(lcd->info.spi, 0xA0); // 横屏显示,并上下翻转,RGB像素格式 lcd->info.x_offset = 80; // 设置控制器坐标偏移量 lcd->info.y_offset = 0; } else if (scan_dir == Direction_V_Flip) { _write_command(lcd->info.spi, 0x36); // 显存访问控制 指令,用于设置访问显存的方式 _write_data_8bit(lcd->info.spi, 0xC0); // 垂直显示 ,并上下翻转,RGB像素格式 lcd->info.x_offset = 0; // 设置控制器坐标偏移量 lcd->info.y_offset = 80; } _wait_finish(lcd->info.spi); // 等待通讯完成 lcd->info.spi->gpios.cs->set(*lcd->info.spi->gpios.cs); } static void _set_address(lcd_t *lcd, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { lcd->info.spi->gpios.cs->reset(*lcd->info.spi->gpios.cs); _write_command(lcd->info.spi, 0x2a); // 列地址设置,即X坐标 _write_data_16bit(lcd->info.spi, x1 + lcd->info.x_offset); _write_data_16bit(lcd->info.spi, x2 + lcd->info.x_offset); _write_command(lcd->info.spi, 0x2b); // 行地址设置,即Y坐标 _write_data_16bit(lcd->info.spi, y1 + lcd->info.y_offset); _write_data_16bit(lcd->info.spi, y2 + lcd->info.y_offset); _write_command(lcd->info.spi, 0x2c); // 开始写入显存,即要显示的颜色数据 _wait_finish(lcd->info.spi); // 等待通讯完成 lcd->info.spi->gpios.cs->set(*lcd->info.spi->gpios.cs); } // 写像素缓冲区 static void _write_pixels(lcd_t *lcd, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t *data) { uint32_t len = (uint32_t)(x2 - x1 + 1) * (y2 - y1 + 1); lcd->driver.set_address(lcd, x1, y1, x2, y2); _write_buff(lcd, data, len); } static void _clear(lcd_t *lcd) { uint32_t i; lcd->driver.set_address(lcd, 0, 0, lcd->info.width - 1, lcd->info.height - 1); lcd->info.spi->spi->Instance->CR1 &= 0xFFBF; // 关闭SPI lcd->info.spi->spi->Instance->CR1 |= 0x0800; // 切换成16位数据格式 lcd->info.spi->spi->Instance->CR1 |= 0x0040; // 使能SPI lcd->info.spi->gpios.cs->reset(*lcd->info.spi->gpios.cs); for (i = 0; i < lcd->info.width * lcd->info.height; i++) { lcd->info.spi->spi->Instance->DR = lcd->info.back_color; _wait_send_empty(lcd->info.spi); // 等待发送缓冲区清空 } _wait_finish(lcd->info.spi); // 等待通讯完成 lcd->info.spi->gpios.cs->set(*lcd->info.spi->gpios.cs); lcd->info.spi->spi->Instance->CR1 &= 0xFFBF; // 关闭SPI lcd->info.spi->spi->Instance->CR1 &= 0xF7FF; // 切换成8位数据格式 lcd->info.spi->spi->Instance->CR1 |= 0x0040; // 使能SPI } static int32_t _init(lcd_t *lcd) { HAL_Delay(10); lcd->info.spi->gpios.cs->reset(*lcd->info.spi->gpios.cs); _write_command(lcd->info.spi, 0x36); // 显存访问控制 指令,用于设置访问显存的方式 _write_data_8bit(lcd->info.spi, 0x00); // 配置成 从上到下、从左到右,RGB像素格式 _write_command(lcd->info.spi, 0x3A); // 接口像素格式 指令,用于设置使用 12位、16位还是18位色 _write_data_8bit(lcd->info.spi, 0x05); // 此处配置成 16位 像素格式 // 接下来很多都是电压设置指令,直接使用厂家给设定值 _write_command(lcd->info.spi, 0xB2); _write_data_8bit(lcd->info.spi, 0x0C); _write_data_8bit(lcd->info.spi, 0x0C); _write_data_8bit(lcd->info.spi, 0x00); _write_data_8bit(lcd->info.spi, 0x33); _write_data_8bit(lcd->info.spi, 0x33); _write_command(lcd->info.spi, 0xB7); // 栅极电压设置指令 _write_data_8bit(lcd->info.spi, 0x35); // VGH = 13.26V,VGL = -10.43V _write_command(lcd->info.spi, 0xBB); // 公共电压设置指令 _write_data_8bit(lcd->info.spi, 0x19); // VCOM = 1.35V _write_command(lcd->info.spi, 0xC0); _write_data_8bit(lcd->info.spi, 0x2C); _write_command(lcd->info.spi, 0xC2); // VDV 和 VRH 来源设置 _write_data_8bit(lcd->info.spi, 0x01); // VDV 和 VRH 由用户自由配置 _write_command(lcd->info.spi, 0xC3); // VRH电压 设置指令 _write_data_8bit(lcd->info.spi, 0x12); // VRH电压 = 4.6+( vcom+vcom offset+vdv) _write_command(lcd->info.spi, 0xC4); // VDV电压 设置指令 _write_data_8bit(lcd->info.spi, 0x20); // VDV电压 = 0v _write_command(lcd->info.spi, 0xC6); // 正常模式的帧率控制指令 _write_data_8bit(lcd->info.spi, 0x0F); // 设置屏幕控制器的刷新帧率为60帧 _write_command(lcd->info.spi, 0xD0); // 电源控制指令 _write_data_8bit(lcd->info.spi, 0xA4); // 无效数据,固定写入0xA4 _write_data_8bit(lcd->info.spi, 0xA1); // AVDD = 6.8V ,AVDD = -4.8V ,VDS = 2.3V _write_command(lcd->info.spi, 0xE0); // 正极电压伽马值设定 _write_data_8bit(lcd->info.spi, 0xD0); _write_data_8bit(lcd->info.spi, 0x04); _write_data_8bit(lcd->info.spi, 0x0D); _write_data_8bit(lcd->info.spi, 0x11); _write_data_8bit(lcd->info.spi, 0x13); _write_data_8bit(lcd->info.spi, 0x2B); _write_data_8bit(lcd->info.spi, 0x3F); _write_data_8bit(lcd->info.spi, 0x54); _write_data_8bit(lcd->info.spi, 0x4C); _write_data_8bit(lcd->info.spi, 0x18); _write_data_8bit(lcd->info.spi, 0x0D); _write_data_8bit(lcd->info.spi, 0x0B); _write_data_8bit(lcd->info.spi, 0x1F); _write_data_8bit(lcd->info.spi, 0x23); _write_command(lcd->info.spi, 0xE1); // 负极电压伽马值设定 _write_data_8bit(lcd->info.spi, 0xD0); _write_data_8bit(lcd->info.spi, 0x04); _write_data_8bit(lcd->info.spi, 0x0C); _write_data_8bit(lcd->info.spi, 0x11); _write_data_8bit(lcd->info.spi, 0x13); _write_data_8bit(lcd->info.spi, 0x2C); _write_data_8bit(lcd->info.spi, 0x3F); _write_data_8bit(lcd->info.spi, 0x44); _write_data_8bit(lcd->info.spi, 0x51); _write_data_8bit(lcd->info.spi, 0x2F); _write_data_8bit(lcd->info.spi, 0x1F); _write_data_8bit(lcd->info.spi, 0x1F); _write_data_8bit(lcd->info.spi, 0x20); _write_data_8bit(lcd->info.spi, 0x23); _write_command(lcd->info.spi, 0x21); // 打开反显,因为面板是常黑型,操作需要反过来 // 退出休眠指令,LCD控制器在刚上电、复位时,会自动进入休眠模式 ,因此操作屏幕之前,需要退出休眠 _write_command(lcd->info.spi, 0x11); // 退出休眠 指令 HAL_Delay(120); // 需要等待120ms,让电源电压和时钟电路稳定下来 // 打开显示指令,LCD控制器在刚上电、复位时,会自动关闭显示 _write_command(lcd->info.spi, 0x29); // 打开显示 _wait_finish(lcd->info.spi); // 等待通讯完成 lcd->info.spi->gpios.cs->set(*lcd->info.spi->gpios.cs); // 以下进行一些驱动的默认设置 lcd->driver.set_dir(lcd, Direction_V); // 默认竖屏显示 lcd->info.back_color = 0x0000; // 默认背景色为黑色 lcd->driver.clear(lcd); // 清除屏幕 return 0; } void lcd_tft_154_init(lcd_driver_t *driver) { DBG_ASSERT(driver != NULL __DBG_LINE); driver->init = _init; // 已实现 driver->set_dir = _set_dir; // 已实现 driver->set_address = _set_address; // 已实现 driver->write_pixels = _write_pixels; // 已实现 driver->clear = _clear; // 已实现 }