freertos_f407/User/system/lib/lcd/lcd_tft_154.c

443 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "lcd_tft_154.h"
#define FILL_ZERO 0 // 填充0
#define FILL_SPACE 1 // 填充空格
// 因为这类SPI的屏幕每次更新显示时需要先配置坐标区域、再写显存
// 在显示字符时,如果是一个个点去写坐标写显存,会非常慢,
// 因此开辟一片缓冲区,先将需要显示的数据写进缓冲区,最后再批量写入显存。
// 用户可以根据实际情况去修改此处缓冲区的大小,
// 例如用户需要显示32*32的汉字时需要的大小为 32*32*2 = 2048 字节每个像素点占2字节
static uint16_t *pri; // 显示缓存
// 等待通讯完成
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_color(lcd_t *lcd, uint32_t color)
{
uint16_t red_value = 0, green_value = 0, blue_value = 0; // 各个颜色通道的值
red_value = (uint16_t)((color & 0x00F80000) >> 8); // 转换成 16位 的RGB565颜色
green_value = (uint16_t)((color & 0x0000FC00) >> 5);
blue_value = (uint16_t)((color & 0x000000F8) >> 3);
lcd->info.color = (uint16_t)(red_value | green_value | blue_value); // 将颜色写入全局LCD参数
}
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 _set_asscii_font(lcd_t *lcd, lcd_font *font)
{
lcd->info.ascii_font = font;
}
static void _set_hz_font(lcd_t *lcd, lcd_font *font)
{
lcd->info.hz_font = font;
switch (font->width)
{
case 12:
lcd->driver.set_asscii_font(lcd, &ascii_font_12);
break; // 设置ASCII字符的字体为 1206
case 16:
lcd->driver.set_asscii_font(lcd, &ascii_font_16);
break; // 设置ASCII字符的字体为 1608
case 20:
lcd->driver.set_asscii_font(lcd, &ascii_font_20);
break; // 设置ASCII字符的字体为 2010
case 24:
lcd->driver.set_asscii_font(lcd, &ascii_font_24);
break; // 设置ASCII字符的字体为 2412
case 32:
lcd->driver.set_asscii_font(lcd, &ascii_font_32);
break; // 设置ASCII字符的字体为 3216
default:
break;
}
}
/****************************************************************************************************************************************
* 函 数 名: LCD_DisplayChar
*
* 入口参数: x - 起始水平坐标
* y - 起始垂直坐标
* c - ASCII字符
*
* 函数功能: 在指定坐标显示指定的字符
*
* 说 明: 1. 可设置要显示的字体,例如使用 LCD_SetAsciiFont(&ASCII_Font24) 设置为 2412的ASCII字体
* 2. 可设置要显示的颜色,例如使用 LCD_SetColor(0xff0000FF) 设置为蓝色
* 3. 可设置对应的背景色,例如使用 LCD_SetBackColor(0x000000) 设置为黑色的背景色
* 4. 使用示例 LCD_DisplayChar( 10, 10, 'a') ,在坐标(10,10)显示字符 'a'
*
*****************************************************************************************************************************************/
void _display_char(lcd_t *lcd, uint16_t x, uint16_t y, uint8_t c)
{
uint16_t index = 0, counter = 0, i = 0, w = 0; // 计数变量
uint8_t disChar; // 存储字符的地址
c = c - 32; // 计算ASCII字符的偏移
lcd->info.spi->gpios.cs->reset(*lcd->info.spi->gpios.cs);
for (index = 0; index < lcd->info.ascii_font->sizes; index++)
{
disChar = lcd->info.ascii_font->table[c * lcd->info.ascii_font->sizes + index]; // 获取字符的模值
for (counter = 0; counter < 8; counter++)
{
if (disChar & 0x01)
{
pri[i] = lcd->info.color; // 当前模值不为0时使用画笔色绘点
}
else
{
pri[i] = lcd->info.back_color; // 否则使用背景色绘制点
}
disChar >>= 1;
i++;
w++;
if (w == lcd->info.ascii_font->width) // 如果写入的数据达到了字符宽度,则退出当前循环
{ // 进入下一字符的写入的绘制
w = 0;
break;
}
}
}
lcd->driver.set_address(lcd, x, y, x + lcd->info.ascii_font->width - 1, y + lcd->info.ascii_font->height - 1); // 设置坐标
_write_buff(lcd, pri, lcd->info.ascii_font->width * lcd->info.ascii_font->height); // 写入显存
}
void _display_chinese(lcd_t *lcd, uint16_t x, uint16_t y, char *text)
{
uint16_t i = 0, index = 0, counter = 0; // 计数变量
uint16_t addr; // 字模地址
uint8_t disChar; // 字模的值
uint16_t Xaddress = 0; // 水平坐标
while (1)
{
// 对比数组中的汉字编码,用以定位该汉字字模的地址
if (*(lcd->info.hz_font->table + (i + 1) * lcd->info.hz_font->sizes + 0) == *text && *(lcd->info.hz_font->table + (i + 1) * lcd->info.hz_font->sizes + 1) == *(text + 1))
{
addr = i; // 字模地址偏移
break;
}
i += 2; // 每个中文字符编码占两字节
if (i >= lcd->info.hz_font->table_rows)
break; // 字模列表中无相应的汉字
}
i = 0;
for (index = 0; index < lcd->info.hz_font->sizes; index++)
{
disChar = *(lcd->info.hz_font->table + (addr)*lcd->info.hz_font->sizes + index); // 获取相应的字模地址
for (counter = 0; counter < 8; counter++)
{
if (disChar & 0x01)
{
pri[i] = lcd->info.color; // 当前模值不为0时使用画笔色绘点
}
else
{
pri[i] = lcd->info.back_color; // 否则使用背景色绘制点
}
i++;
disChar >>= 1;
Xaddress++; // 水平坐标自加
if (Xaddress == lcd->info.hz_font->width) // 如果水平坐标达到了字符宽度,则退出当前循环
{ // 进入下一行的绘制
Xaddress = 0;
break;
}
}
}
lcd->driver.set_address(lcd, x, y, x + lcd->info.hz_font->width - 1, y + lcd->info.hz_font->height - 1); // 设置坐标
_write_buff(lcd, pri, lcd->info.hz_font->width * lcd->info.hz_font->height); // 写入显存
}
static void _display_text(lcd_t *lcd, uint16_t x, uint16_t y, char *text)
{
while (*text != 0) // 判断是否为空字符
{
if (*text <= 0x7F) // 判断是否为ASCII码
{
_display_char(lcd, x, y, *text); // 显示ASCII
x += lcd->info.ascii_font->width; // 水平坐标调到下一个字符处
text++; // 字符串地址+1
}
else // 若字符为汉字
{
_display_chinese(lcd, x, y, text); // 显示汉字
x += lcd->info.hz_font->width; // 水平坐标调到下一个字符处
text += 2; // 字符串地址+2汉字的编码要2字节
}
}
}
// 设置背景色
static void _full_fill(lcd_t *lcd, uint32_t color)
{
uint16_t red_value = 0, green_value = 0, blue_value = 0; // 各个颜色通道的值
red_value = (uint16_t)((color & 0x00F80000) >> 8); // 转换成 16位 的RGB565颜色
green_value = (uint16_t)((color & 0x0000FC00) >> 5);
blue_value = (uint16_t)((color & 0x000000F8) >> 3);
lcd->info.back_color = (uint16_t)(red_value | green_value | blue_value); // 将颜色写入全局LCD参数
}
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);
pri = (uint16_t *)osel_mem_alloc2(1024 * sizeof(lcd));
lcd->info.pri = (void *)pri;
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.26VVGL = -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->driver.full_fill(lcd, LCD_BLACK); // 默认背景色为黑色
lcd->driver.set_color(lcd, LCD_WHITE); // 默认画笔颜色为白色
lcd->driver.clear(lcd); // 清除屏幕
lcd->driver.set_asscii_font(lcd, &ascii_font_24); // 默认ASCII字体
lcd->info.show_num_mode = FILL_ZERO; // 设置变量显示模式多余位填充空格还是填充0
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_color = _set_color; // 已实现
driver->set_address = _set_address; // 已实现
driver->set_asscii_font = _set_asscii_font; // 已实现
driver->set_hz_font = _set_hz_font; // 已实现
driver->display_text = _display_text; // 已实现
driver->clear = _clear; // 已实现
driver->full_fill = _full_fill; // 已实现
}