📄 tft_lcd.c
字号:
/****************************************************************************
* Copyright (C), 2009-2010, www.armfly.com
*
* 文件名: tft_lcd.c
* 内容简述: 本模块包含TFT LCD显示器的驱动函数。
*
* 安富莱开发板标配的TFT显示器的驱动芯片为 SPFD5420A,分辨率为400x240
* 驱动芯片的访问地址为
* (1) 0x60000000
*
* 文件历史:
* 版本号 日期 作者 说明
* v0.1 2010-01-02 armfly 创建该文件
*
*/
#include "stm32f10x.h"
#include <stdio.h>
#include "systick.h"
#include "tft_lcd.h"
#include "fonts.h"
typedef struct
{
__IO uint16_t LCD_REG;
__IO uint16_t LCD_RAM;
}
LCD_TypeDef;
/* 定义LCD驱动器的访问地址 */
#define LCD_BASE ((uint32_t)(0x60000000 | 0x0C000000))
#define LCD ((LCD_TypeDef *) LCD_BASE)
static __IO uint16_t s_TextColor = 0x0000;
static __IO uint16_t s_BackColor = 0xFFFF;
/* armfly添加透明标志
s_Transparent = 1 表示显示文字时,不修改背景颜色
*/
static __IO uint8_t s_Transparent = 0;
/*******************************************************************************
* 函数名: LCD_Init
* 参 数: 无
* 返 回: 无
* 功 能: 初始化TFT显示器
*/
void LCD_Init(void)
{
/* 配置LCD控制口线GPIO */
LCD_CtrlLinesConfig();
/* 配置FSMC接口,数据总线 */
LCD_FSMCConfig();
/* FSMC重置后必须加延迟才能访问总线设备 */
DelayMS(20);
/* 初始化LCD,写LCD寄存器进行配置 */
LCD_WriteReg(0x0000, 0x0000);
LCD_WriteReg(0x0001, 0x0100);
LCD_WriteReg(0x0002, 0x0100);
/*
R003H 寄存器很关键, Entry Mode ,决定了扫描方向
参见:SPFD5420A.pdf 第15页
240x400屏幕物理坐标(px,py)如下:
R003 = 0x1018 R003 = 0x1008
------------------- -------------------
|(0,0) | |(0,0) |
| | |
| ^ ^ | | ^ ^ |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | ------> | | | | <------ | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | |
| | | |
| (x=239,y=399)| | (x=239,y=399)|
|-------------------| |-------------------|
| | | |
------------------- -------------------
按照安富莱开发板LCD的方向,我们期望的虚拟坐标和扫描方向如下:(和上图第1个吻合)
--------------------------------
| |(0,0) |
| | ---------> |
| | | |
| | | |
| | | |
| | V |
| | ---------> |
| | (399,239)|
--------------------------------
虚拟坐标(x,y) 和物理坐标的转换关系
x = 399 - py;
y = px;
py = 399 - x;
px = y;
*/
LCD_WriteReg(0x0003, 0x1018); /* 0x1018 1030 */
LCD_WriteReg(0x0008, 0x0808);
LCD_WriteReg(0x0009, 0x0001);
LCD_WriteReg(0x000B, 0x0010);
LCD_WriteReg(0x000C, 0x0000);
LCD_WriteReg(0x000F, 0x0000);
LCD_WriteReg(0x0007, 0x0001);
LCD_WriteReg(0x0010, 0x0013);
LCD_WriteReg(0x0011, 0x0501);
LCD_WriteReg(0x0012, 0x0300);
LCD_WriteReg(0x0020, 0x021E);
LCD_WriteReg(0x0021, 0x0202);
LCD_WriteReg(0x0090, 0x8000);
LCD_WriteReg(0x0100, 0x17B0);
LCD_WriteReg(0x0101, 0x0147);
LCD_WriteReg(0x0102, 0x0135);
LCD_WriteReg(0x0103, 0x0700);
LCD_WriteReg(0x0107, 0x0000);
LCD_WriteReg(0x0110, 0x0001);
LCD_WriteReg(0x0210, 0x0000);
LCD_WriteReg(0x0211, 0x00EF);
LCD_WriteReg(0x0212, 0x0000);
LCD_WriteReg(0x0213, 0x018F);
LCD_WriteReg(0x0280, 0x0000);
LCD_WriteReg(0x0281, 0x0004);
LCD_WriteReg(0x0282, 0x0000);
LCD_WriteReg(0x0300, 0x0101);
LCD_WriteReg(0x0301, 0x0B2C);
LCD_WriteReg(0x0302, 0x1030);
LCD_WriteReg(0x0303, 0x3010);
LCD_WriteReg(0x0304, 0x2C0B);
LCD_WriteReg(0x0305, 0x0101);
LCD_WriteReg(0x0306, 0x0807);
LCD_WriteReg(0x0307, 0x0708);
LCD_WriteReg(0x0308, 0x0107);
LCD_WriteReg(0x0309, 0x0105);
LCD_WriteReg(0x030A, 0x0F04);
LCD_WriteReg(0x030B, 0x0F00);
LCD_WriteReg(0x030C, 0x000F);
LCD_WriteReg(0x030D, 0x040F);
LCD_WriteReg(0x030E, 0x0300);
LCD_WriteReg(0x030F, 0x0701);
LCD_WriteReg(0x0400, 0x3500);
LCD_WriteReg(0x0401, 0x0001);
LCD_WriteReg(0x0404, 0x0000);
LCD_WriteReg(0x0500, 0x0000);
LCD_WriteReg(0x0501, 0x0000);
LCD_WriteReg(0x0502, 0x0000);
LCD_WriteReg(0x0503, 0x0000);
LCD_WriteReg(0x0504, 0x0000);
LCD_WriteReg(0x0505, 0x0000);
LCD_WriteReg(0x0600, 0x0000);
LCD_WriteReg(0x0606, 0x0000);
LCD_WriteReg(0x06F0, 0x0000);
LCD_WriteReg(0x07F0, 0x5420);
LCD_WriteReg(0x07DE, 0x0000);
LCD_WriteReg(0x07F2, 0x00DF);
LCD_WriteReg(0x07F3, 0x0810);
LCD_WriteReg(0x07F4, 0x0077);
LCD_WriteReg(0x07F5, 0x0021);
LCD_WriteReg(0x07F0, 0x0000);
LCD_WriteReg(0x0007, 0x0173);
/* 设置显示窗口 WINDOWS */
LCD_WriteReg(0x0210, 0); /* 水平起始地址 */
LCD_WriteReg(0x0211, 239); /* 水平结束坐标 */
LCD_WriteReg(0x0212, 0); /* 垂直起始地址 */
LCD_WriteReg(0x0213, 399); /* 垂直结束地址 */
}
/*******************************************************************************
* 函数名: LCD_SetTextColor
* 参 数: Color : 文本颜色
* 返 回: 无
* 功 能: 设置文本颜色,保存在全部变量s_TextColor
*/
void LCD_SetTextColor(__IO uint16_t Color)
{
s_TextColor = Color;
}
/*******************************************************************************
* 函数名: LCD_SetBackColor
* 参 数: Color : 背景颜色
* 返 回: 无
* 功 能: 设置背景颜色,保存在全部变量 s_BackColor
*/
void LCD_SetBackColor(__IO uint16_t Color)
{
s_BackColor = Color;
}
/*******************************************************************************
* 函数名: LCD_ClearLine
* 参 数: Line : 文本行号0 - 9
* 返 回: 无
* 功 能: 清除选定的文本行
*/
void LCD_ClearLine(uint8_t Line)
{
LCD_DisplayString(0, Line, " ");
}
/*******************************************************************************
* 函数名: LCD_Clear
* 参 数: Color : 背景色
* 返 回: 无
* 功 能: 根据输入的颜色值清屏
*/
void LCD_Clear(uint16_t Color)
{
uint32_t index = 0;
LCD_SetCursor(0, 0); /* 设置光标位置 */
LCD_WriteRAM_Prepare(); /* 准备写显存 */
for (index = 0; index < 400 * 240; index++)
{
LCD->LCD_RAM = Color;
}
}
/*******************************************************************************
* 函数名: LCD_SetCursor
* 参 数: Xpos : X坐标; Ypos: Y坐标
* 返 回: 无
* 功 能: 设置光标位置
*/
void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
/*
px,py 是物理坐标, x,y是虚拟坐标
转换公式:
py = 399 - x;
px = y;
*/
LCD_WriteReg(0x0200, Ypos); /* px */
LCD_WriteReg(0x0201, 399 - Xpos); /* py */
}
/*******************************************************************************
* 函数名: LCD_SetTransparent
* 参 数: _mode ; 透明标志,1表示透明 0 表示不透明
* 返 回: 无
* 功 能: 设置光标位置
*/
void LCD_SetTransparent(uint8_t _mode)
{
if (_mode == 0)
{
s_Transparent = 0; /* 设置为背景不透明 */
}
else
{
s_Transparent = 1; /* 设置为背景透明 */
}
}
/*******************************************************************************
* 函数名: LCD_DrawChar
* 参 数:
* Xpos : X坐标;
* Ypos: Y坐标;
* c : 指向字符点阵的指针
* width : 点阵的宽度,字符是8,汉字是16
* 返 回: 无
* 功 能: 在LCD上显示一个字符(16x24)
*/
void LCD_DrawChar(uint16_t Xpos, uint16_t Ypos, const uint8_t *c, uint8_t width)
{
uint32_t index = 0, i = 0;
uint8_t Yaddress;
Yaddress = Ypos;
LCD_SetCursor(Xpos, Ypos);
if (s_Transparent == 0)
{
for (index = 0; index < 16; index++) /* 字符高度 */
{
LCD_WriteRAM_Prepare();
for (i = 0; i < width; i++) /* 字符宽度 */
{
if ((c[index * (width / 8) + i / 8] & (0x80 >> (i % 8))) == 0x00)
{
LCD_WriteRAM(s_BackColor);
}
else
{
LCD_WriteRAM(s_TextColor);
}
}
Yaddress++;
LCD_SetCursor(Xpos, Yaddress);
}
}
else /* armfly 添加:实现文字叠加在图片上的功能 */
{
for (index = 0; index < 16; index++) /* 字符高度 */
{
uint16_t x = Xpos;
for (i = 0; i < width; i++) /* 字符宽度 */
{
if ((c[index * (width / 8) + i / 8] & (0x80 >> (i % 8))) != 0x00)
{
LCD_SetCursor(x, Yaddress);
LCD->LCD_REG = 0x202;
LCD->LCD_RAM = s_TextColor;
}
x++;
}
Yaddress++;
}
}
}
/*******************************************************************************
* 函数名: LCD_DisplayString
* 参 数:
* Xpos : X坐标 0 - 399
* Ypos : Y坐标 0 - 239
* ptr : 字符串指针;
* 返 回: 无
* 功 能: 在LCD指定文本行显示一个字符串
*/
void LCD_DisplayString(uint16_t Xpos, uint16_t Ypos, uint8_t *ptr)
{
uint32_t i = 0;
uint8_t code1;
uint8_t code2;
uint32_t address;
uint32_t k;
while ((*ptr != 0) & (i < 50))
{
code1 = *ptr; /* ascii代码 或者汉字代码的高字节 */
if (code1 < 0x80)
{
LCD_DrawChar(Xpos, Ypos, &Ascii16[code1 * 16], 8); /* 16 表示1个字符字模的字节数 */
Xpos += 8; /* 列地址+8 */
}
else /* 汉字内码 */
{
code2 = *++ptr;
if (code2 == 0)
{
break;
}
/* 计算16点阵汉字点阵地址
ADDRESS = [(code1-0xa1) * 94 + (code2-0xa1)] * 32
;
*/
#ifdef USE_SMALL_FONT
for (k = 0; k < HZ_COUNT; k++)
{
address = k * 34;
if ((code1 == g_Hz16[address + 0]) && (code2 == g_Hz16[address + 1]))
{
address += 2;
break;
}
}
address = (uint32_t)&g_Hz16[address];
#else
address = ((code1-0xa1) * 94 + (code2-0xa1)) * 32 + HZK16_ADDR;
#endif
LCD_DrawChar(Xpos, Ypos, (const uint8_t *)address, 16);
Xpos += 16; /* 列地址+16 */
}
ptr++; /* 指向下一个字符 */
i++;
}
}
/*******************************************************************************
* 函数名: LCD_SetDisplayWindow
* 参 数:
* Xpos : 显示行号
* Ypos : 字符串指针;
* Height: 窗口高度
* Width : 窗口宽度
* 返 回: 无
* 功 能: 设置显示窗口
*/
void LCD_SetDisplayWindow(uint16_t Xpos, uint16_t Ypos, uint16_t Height, uint16_t Width)
{
/* 水平起始地址 */
if (Xpos >= Height)
{
LCD_WriteReg(R80, (Xpos - Height + 1));
}
else
{
LCD_WriteReg(R80, 0);
}
/* 水平结束地址 */
LCD_WriteReg(R81, Xpos);
/* 垂直起始地址 */
if (Ypos >= Width)
{
LCD_WriteReg(R82, (Ypos - Width + 1));
}
else
{
LCD_WriteReg(R82, 0);
}
/* 垂直结束地址 */
LCD_WriteReg(R83, Ypos);
LCD_SetCursor(Xpos, Ypos);
}
/*******************************************************************************
* 函数名: LCD_WindowModeDisable
* 参 数: 无
* 返 回: 无
* 功 能: 退出窗口显示模式,变为全屏显示模式
*/
void LCD_WindowModeDisable(void)
{
LCD_SetDisplayWindow(239, 399, 240, 400);
LCD_WriteReg(R3, 0x1018); /* 这个寄存器改变扫描方向 */
}
/*******************************************************************************
* 函数名: LCD_PutPixel
* 参 数: x,y : 像素坐标
* Color : 像素颜色
* 返 回: 无
* 功 能: 画1个像素
*/
void LCD_PutPixel(uint16_t x, uint16_t y, uint16_t Color)
{
LCD_SetCursor(x, y); /* 设置光标位置 */
LCD_WriteRAM1(Color);
}
/*******************************************************************************
* 函数名: BresenhamLine
* 参 数: x1, y1 :起始点坐标
* x2, y2 :终止点Y坐标
* Length :长度
* Direction :方向(Horizontal,Vertical)
* 返 回: 无
* 功 能: 在两点间画一条直线。
*/
void LCD_BresenhamLine (uint16_t x1 , uint16_t y1 , uint16_t x2 , uint16_t y2 , int c)
{
int dx , dy ;
int tx , ty ;
int inc1 , inc2 ;
int d , iTag ;
int x , y ;
LCD_PutPixel(x1 , y1 , c);
/* 如果两点重合,结束后面的动作。*/
if ( x1 == x2 && y1 == y2 )
{
return;
}
iTag = 0 ;
/* dx = abs ( x2 - x1 ); */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -