260 lines
10 KiB
C
260 lines
10 KiB
C
#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; // 已实现
|
||
}
|