本文转自:http://blog.sina.com.cn/s/blog_6cd2030b01019ovi.html
STM32学习笔记——TFT2.4彩屏显示字符和汉字
利用彩屏显示汉字、字符和字符串,需要先完成彩屏的驱动程序,然后在驱动程序的基础上再编写应用程序。
彩屏的驱动程序如果写好的话,就可以一直使用了,精力主要集中在应用程序的编写就可以了,但是移植的话,要移植驱动程序。其实移植也只是改变那些很底层的靠近处理器的那部分代码。
代码如下,注释比较详细,看懂了基本就可以了。
驱动程序的头文件LCD_driver.h如下:
#define uchar unsigned char
#define uint unsigned int
#define Bus_16 //16位数据模式,如果使用8位模式,请注释此语句,如果使用16位模式,请打开此句
#define LCD_DataPortH P1 //高8位数据口,8位模式下只使用高8位
#define LCD_DataPortL P0 //低8位数据口,8位模式下低8位可以不接线
#define LCD_WR GPIO_Pin_2 //WR 引脚定义 P2^5
#define LCD_RS GPIO_Pin_1 //RS 引脚定义 P2^6
#define LCD_CS GPIO_Pin_0 //CS 引脚定义 P2^7
#define LCD_RST GPIO_Pin_11 //RST 引脚定义 P3^3
#define LCD_RD GPIO_Pin_8 //RD 引脚定义 P3^2
#define LCD_SIZE_X240 //彩屏的分辨率为240*320
#define LCD_SIZE_Y320
extern uint colors[];
void LCD_Init(void); //LCD初始化函数
void LCD_ShowString(uint x,uint y,uint For_color,uint Bk_color,char *p);//写字符串函数
void LCD_ShowChar(uint x,uint y,uint For_color,uint Bk_color, char ch);//写字符函数
void ChineseChar(uint x,uint y,int size,uint For_color,uint Bk_color ,char c); //写汉字函数
void delayms(int count) ;
//void LCD_Writ_Bus(char VH,char VL);//8位模式下的总线写函数
void LCD_Writ_Bus( uint VH, uint VL); //16位模式下的总线写函数
void LCD_Write_COM(char VH,char VL); //写命令函数
void LCD_Write_DATA(char VH,char VL); //写数据函数
void Pant(char VH,char VL); //涂满彩屏函数
void Address_set(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2); //设置地址范围函数
驱动程序Lcd.c文件如下:
#include "stm32f10x_lib.h"
#include"LCD_driver.h"
#include"chinese.h"
#include"char.h"
#define LCD_rest(x) x ? GPIO_SetBits(GPIOA, LCD_RST): GPIO_ResetBits(GPIOA, LCD_RST) //自己定义位操作函数
#define LCD_rd(x) x ? GPIO_SetBits(GPIOA, LCD_RD) : GPIO_ResetBits(GPIOA, LCD_RD)
#define LCD_rs(x) x ? GPIO_SetBits(GPIOB, LCD_RS) : GPIO_ResetBits(GPIOB, LCD_RS)
#define LCD_wr(x) x ? GPIO_SetBits(GPIOB, LCD_WR) : GPIO_ResetBits(GPIOB, LCD_WR)
#define LCD_cs(x) x ? GPIO_SetBits(GPIOB, LCD_CS) : GPIO_ResetBits(GPIOB, LCD_CS)
uint colors[]=
{
0xf800,0x07e0,0x001f,0xffe0,0x0000,0x07ff,0xf81f,0xffff //颜色数据 是16位的
};
void LCD_Init(void) //lcd初始化
{
LCD_rest(1);
delayms(5);
LCD_rest(0);
delayms(5);
LCD_rest(1);
delayms(5);
LCD_cs(0); //打开片选使能
//
LCD_Write_COM(0x00,0xE5); LCD_Write_DATA(0x78,0xF0); // set SRAM internal timing
LCD_Write_COM(0x00,0x01); LCD_Write_DATA(0x01,0x00); // set SS and SM bit
LCD_Write_COM(0x00,0x02); LCD_Write_DATA(0x07,0x00); // set 1 line inversion
LCD_Write_COM(0x00,0x03); LCD_Write_DATA(0x10,0x30); // set GRAM write direction and BGR=1.
LCD_Write_COM(0x00,0x04); LCD_Write_DATA(0x00,0x00); // Resize register
LCD_Write_COM(0x00,0x08); LCD_Write_DATA(0x02,0x07); // set the back porch and front porch
LCD_Write_COM(0x00,0x09); LCD_Write_DATA(0x00,0x00); // set non-display area refresh cycle ISC[3:0]
LCD_Write_COM(0x00,0x0A); LCD_Write_DATA(0x00,0x00); // FMARK function
LCD_Write_COM(0x00,0x0C); LCD_Write_DATA(0x00,0x00); // RGB interface setting
LCD_Write_COM(0x00,0x0D); LCD_Write_DATA(0x00,0x00); // Frame marker Position
LCD_Write_COM(0x00,0x0F); LCD_Write_DATA(0x00,0x00); // RGB interface polarity
//
LCD_Write_COM(0x00,0x10); LCD_Write_DATA(0x00,0x00); // SAP, BT[3:0], AP, DSTB, SLP, STB
LCD_Write_COM(0x00,0x11); LCD_Write_DATA(0x00,0x07); // DC1[2:0], DC0[2:0], VC[2:0]
LCD_Write_COM(0x00,0x12); LCD_Write_DATA(0x00,0x00); // VREG1OUT voltage
LCD_Write_COM(0x00,0x13); LCD_Write_DATA(0x00,0x00); // VDV[4:0] for VCOM amplitude
LCD_Write_COM(0x00,0x07); LCD_Write_DATA(0x00,0x01);
delayms(50); // Dis-charge capacitor power voltage
LCD_Write_COM(0x00,0x10); LCD_Write_DATA(0x10,0x90); // 1490//SAP, BT[3:0], AP, DSTB, SLP, STB
LCD_Write_COM(0x00,0x11); LCD_Write_DATA(0x02,0x27); // DC1[2:0], DC0[2:0], VC[2:0]
delayms(50); // Delay 50ms
LCD_Write_COM(0x00,0x12); LCD_Write_DATA(0x00,0x1F); //001C// Internal reference voltage= Vci;
delayms(50); // Delay 50ms
LCD_Write_COM(0x00,0x13); LCD_Write_DATA(0x15,0x00); //0x1000//1400 Set VDV[4:0] for VCOM amplitude 1A00
LCD_Write_COM(0x00,0x29); LCD_Write_DATA(0x00,0x27); //0x0012 //001a Set VCM[5:0] for VCOMH //0x0025 0034
LCD_Write_COM(0x00,0x2B); LCD_Write_DATA(0x00,0x0D); // Set Frame Rate 000C
delayms(50); // Delay 50ms
LCD_Write_COM(0x00,0x20); LCD_Write_DATA(0x00,0x00); // GRAM horizontal Address
LCD_Write_COM(0x00,0x21); LCD_Write_DATA(0x00,0x00); // GRAM Vertical Address
// ----------- Adjust the Gamma Curve ----------//
LCD_Write_COM(0x00,0x30); LCD_Write_DATA(0x00,0x00);
LCD_Write_COM(0x00,0x31); LCD_Write_DATA(0x07,0x07);
LCD_Write_COM(0x00,0x32); LCD_Write_DATA(0x03,0x07);
LCD_Write_COM(0x00,0x35); LCD_Write_DATA(0x02,0x00);
LCD_Write_COM(0x00,0x36); LCD_Write_DATA(0x00,0x08);//0207
LCD_Write_COM(0x00,0x37); LCD_Write_DATA(0x00,0x04);//0306
LCD_Write_COM(0x00,0x38); LCD_Write_DATA(0x00,0x00);//0102
LCD_Write_COM(0x00,0x39); LCD_Write_DATA(0x07,0x07);//0707
LCD_Write_COM(0x00,0x3C); LCD_Write_DATA(0x00,0x02);//0702
LCD_Write_COM(0x00,0x3D); LCD_Write_DATA(0x1D,0x04);//1604
//------------------ Set GRAM area ---------------//
LCD_Write_COM(0x00,0x50); LCD_Write_DATA(0x00,0x00); // Horizontal GRAM Start Address
LCD_Write_COM(0x00,0x51); LCD_Write_DATA(0x00,0xEF); // Horizontal GRAM End Address
LCD_Write_COM(0x00,0x52); LCD_Write_DATA(0x00,0x00); // Vertical GRAM Start Address
LCD_Write_COM(0x00,0x53); LCD_Write_DATA(0x01,0x3F); // Vertical GRAM Start Address
LCD_Write_COM(0x00,0x60); LCD_Write_DATA(0xA7,0x00); // Gate Scan Line
LCD_Write_COM(0x00,0x61); LCD_Write_DATA(0x00,0x01); // NDL,VLE, REV
LCD_Write_COM(0x00,0x6A); LCD_Write_DATA(0x00,0x00); // set scrolling line
//-------------- Partial Display Control ---------//
LCD_Write_COM(0x00,0x80); LCD_Write_DATA(0x00,0x00);
LCD_Write_COM(0x00,0x81); LCD_Write_DATA(0x00,0x00);
LCD_Write_COM(0x00,0x82); LCD_Write_DATA(0x00,0x00);
LCD_Write_COM(0x00,0x83); LCD_Write_DATA(0x00,0x00);
LCD_Write_COM(0x00,0x84); LCD_Write_DATA(0x00,0x00);
LCD_Write_COM(0x00,0x85); LCD_Write_DATA(0x00,0x00);
//-------------- Panel Control -------------------//
LCD_Write_COM(0x00,0x90); LCD_Write_DATA(0x00,0x10);
LCD_Write_COM(0x00,0x92); LCD_Write_DATA(0x06,0x00);
LCD_Write_COM(0x00,0x07); LCD_Write_DATA(0x01,0x33); // 262K color and display ON
LCD_cs(1); //关闭片选使能
}
void delayms(int count) //
{
int i,j;
for(i=0;i<count;i++)
for(j=0;j<100;j++);
}
#ifdef Bus_16 //条件编译-16位数据模式
//并行写入数据把数据分为高8位VH和低8位VL
void LCD_Writ_Bus(uint VH , uint VL) //并行数据写入函数
{
//LCD_DataPortH=VH; //高位P1口
//屏蔽高8位 将数据送到PA0-PA7
GPIOA->BSRR = VH & 0x00ff;
GPIOA->BRR = (~VH) & 0x00ff;
//GPIO_SetBits(GPIOA, VH & 0x00ff);
//GPIO_ResetBits(GPIOA, (~ VH & 0x00ff));
//LCD_DataPortL=VL; //低位P0口
//屏蔽低8位 将数据送到PB8-PB15
GPIOB->BSRR = (VL)<<8 & 0xff00;
GPIOB->BRR = ((~VL)<<8) & 0xff00;
LCD_wr(0);
LCD_wr(1);
}
#else //条件编译-8位数据模式
void LCD_Writ_Bus(char VH,char VL) //并行数据写入函数
{
//LCD_DataPortH=VH; //八位模式都用P0口送数据
//LCD_wr(0);
//LCD_wr(1);
//LCD_DataPortH=VL; //八位模式都用P0口送数据
//LCD_wr(0);
//LCD_wr(1);
}
#endif
//参数VH,VL分别为要写入命令的高8位和低8位
void LCD_Write_COM(char VH,char VL) //发送命令
{
LCD_rs(0);
LCD_Writ_Bus(VH,VL);
}
//参数VH,VL分别为要写入数据的高8位和低8位
void LCD_Write_DATA(char VH,char VL)//发送数据
{
LCD_rs(1);
LCD_Writ_Bus(VH,VL);
}
void Pant(char VH,char VL) //涂满全屏函数
{
int i,j;
LCD_cs(0); //打开片选使能
Address_set(0,0,239,319); //设置地址范围为全屏
for(i=0;i<320;i++)
{
for (j=0;j<240;j++)
{
LCD_Write_DATA(VH,VL);
}
}
LCD_cs(1); //关闭片选使能
}
void Address_set(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2) //设置地址范围函数
{
LCD_Write_COM(0x00,0x20);LCD_Write_DATA(x1>>8,x1); //设置X坐标位置
LCD_Write_COM(0x00,0x21);LCD_Write_DATA(y1>>8,y1); //设置Y坐标位置
LCD_Write_COM(0x00,0x50);LCD_Write_DATA(x1>>8,x1); //开始X
LCD_Write_COM(0x00,0x52);LCD_Write_DATA(y1>>8,y1); //开始Y
LCD_Write_COM(0x00,0x51);LCD_Write_DATA(x2>>8,x2); //结束X
LCD_Write_COM(0x00,0x53);LCD_Write_DATA(y2>>8,y2); //结束Y
LCD_Write_COM(0x00,0x22);
}
void ChineseChar(uint x,uint y,int size,uint For_color,uint Bk_color ,char c)
{
int e=0,i,j;
int ed;
uint ncols;
uint nrows;
uint nbytes;
uchar *pfont; //指向指定大小的汉字数据存储的数组的指针
uchar *pchar; //指向指定数组中指定要显示的汉字的数据的指针
uchar *fonttable[]={(uchar *)hz16,(uchar *)hz24 , (uchar *)hz32}; //汉字的三种大小,16位,24位,32位
//其中hz16,hz24,hz32为三个数组,分别存储宋体12,宋体18和宋体24的汉字的数据
//每个数组中的数据可能包含相同大小字体的多个汉字
pfont=(uchar *)fonttable[size]; //指向指定大小的汉字的数组
if(size==0)
{
ncols =16-1;
nrows =16-1;
nbytes =32; //每个字取模的字节数=(16*16)/8
}
else
if(size==1)
{
ncols =24-1;
nrows =24-1;
nbytes =72; //每个字取模的字节数=(24*24)/8
}
else
if(size==2)
{
ncols =32-1;
nrows =32-1;
nbytes =132; //每个字取模的字节数=(32*32)/8
}
pchar=pfont + (nbytes*(c-1)); //指定要显示指定数组中的第c个汉字
//每个数组中的数据可能包含相同大小字体的多个汉字
Address_set(x, y, x+nrows,y+ncols); //设置汉字显示的范围
for(i=0;i<nbytes;i++) //每个汉字含有nbytes个字节,所以要循环nbytes次
{
ed=*pchar++; //ed取出指定要显示的字节的数据
if(size==0) //???
{
if(i%2==0) //???
e=8;
else
e=4; //???
}
else
e=8;
for(j=0;j<e;j++)
{
if((ed>>j)&0x01) //判断是汉字还是背景,然后分别写入不同的颜色
{
LCD_Write_DATA(For_color>>8 , For_color ); //textcolor
}
else
{
LCD_Write_DATA( Bk_color>>8 , Bk_color); //backcolor
}
}
}
}
void LCD_ShowChar(uint x,uint y,uint For_color,uint Bk_color, char ch)
{
uchar temp , Bytes;
uchar pos,t;
uint CHAR_W,CHAR_H; //一个字符的大小
//CHAR_W = 8; //8*16
//CHAR_H = 16;
CHAR_W = 16; //16*24 字符的大小为16*24
CHAR_H = 24;
if(x>(LCD_SIZE_X-CHAR_W)||y>(LCD_SIZE_Y-CHAR_H)) //如果指定的字符显示的位置超过了彩屏的分辨率,则返回
return;
Address_set(x, y, x+CHAR_W-1, y+CHAR_H-1); //设置字符显示的地址范围
ch = ch-32; //按照ASCII编码顺序得到相应字母的编码
Bytes = (CHAR_W*CHAR_H)/8; //每个字母取模字节数 一个字节为8位,也即是8个像素点是一个字节
for(pos=0;pos<Bytes;pos++) //一个字节一个字节的写入,知道这个字符的全部字节数写完
{
// temp= Font8x16[ch][pos]; //8*16的字体
temp= Font16x24[ch][pos]; //字体的大小为16*24
for(t=0;t<8;t++) //一个字节为8位,所以要循环8次写入,一次只能写入一个像素点
{
//判断哪里是显示字符的地方,哪里是背景,然后分别写入不同的颜色就可以了
if(temp&0x80) //如果是显示字符的地方
LCD_Write_DATA(For_color>>8 , For_color ); //textcolor
else //否则就是背景的地方
LCD_Write_DATA(Bk_color>>8 , Bk_color ); //backcolor
temp<<=1; //右移一位
}
}
}
void LCD_ShowString(uint x,uint y,uint For_color,uint Bk_color,char *p)
{
while(*p!='\0')//判断是否到了字符串尾
{
LCD_ShowChar(x,y,For_color,Bk_color,*p);
// x+=8; //8是字体的宽度
x+=16; //16是字体的宽度
p++; //指针后移一位
}
}
主程序main.c
在主程序中完成对系统的初始化配置,利用彩屏的驱动程序完成相应的显示等功能,程序如下:
#include "stm32f10x_lib.h"
#include "LCD_driver.h"
#define LCD_cs(x) x ? GPIO_SetBits(GPIOB, LCD_CS) : GPIO_ResetBits(GPIOB, LCD_CS)//自己定义的位函数 对CS位的操作
//打开或关闭片选使能 LCD_cs(0); //打开片选使能 LCD_cs(1); //关闭片选使能
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO初始化的结构体变量
ErrorStatus HSEStartUpStatus; //定义错误状态变量,是枚举类型
void RCC_Configuration(void);
void NVIC_Configuration(void);
void Delay(vu32 nCount);
int main(void)
{
#ifdef DEBUG
debug();
#endif
RCC_Configuration(); //系统时钟配置函数
NVIC_Configuration(); //NVIC配置函数
//启动GPIO模块时钟 使能APB2
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
//把调试设置普通IO口 禁止SWJ
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //所有GPIO为同一类型端口 选中所有的IO口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出的最大频率为50HZ
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA端口
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB端口
GPIO_Write(GPIOA,0xffff); //将GPIOA 16个端口全部置为高电平
GPIO_Write(GPIOB,0xffff); //将GPIOB 16个端口全部置为高电平
LCD_Init(); //彩屏初始化
Pant(colors[5]>>8 , colors[5] ); //把全屏涂成color[5]颜色
LCD_cs(0); //打开片选使能
//下面是要显示的内容
ChineseChar(14+46,10,2,colors[2],colors[7],1);
ChineseChar(46+46,10,2,colors[2],colors[7],2);
ChineseChar(78+46,10,2,colors[2],colors[7],3);
ChineseChar(110+46,10,2,colors[2],colors[7],4);
LCD_ShowString(12,60,colors[0],colors[7],"-");
ChineseChar(20,60,1,colors[0],colors[7],1);//带
ChineseChar(44,60,1,colors[0],colors[7],2);//您
ChineseChar(68,60,1,colors[0],colors[7],3);//进
ChineseChar(92,60,1,colors[0],colors[7],4);//入
ChineseChar(116,60,1,colors[0],colors[7],5);//单
ChineseChar(140,60,1,colors[0],colors[7],6);//片
ChineseChar(164,60,1,colors[0],colors[7],7);//机
ChineseChar(188,60,1,colors[0],colors[7],8);//世
ChineseChar(212,60,1,colors[0],colors[7],9);//界
ChineseChar(0,120,1,colors[4],colors[7],10);//专
ChineseChar(24,120,1,colors[4],colors[7],11); //业
ChineseChar(48,120,1,colors[6],colors[7],12); //技
ChineseChar(72,120,1,colors[6],colors[7],13); //术
ChineseChar(96,120,1,colors[2],colors[7],14); //论
ChineseChar(120,120,1,colors[2],colors[7],15); //坛
// LCD_ShowString(92,120,colors[4],colors[7],":");
LCD_ShowString(0,240,colors[2],colors[7],"abcdefJhiJKLMNOP");
LCD_ShowString(0,265,colors[2],colors[7],"pqrstuvwxyz!@#$");
LCD_ShowString(0,290,colors[2],colors[7],"<>CDEF^&*()/,[]");
LCD_cs(1);//关闭片选使能
while(1);
}
void RCC_Configuration(void)
{
//复位RCC外部设备寄存器到默认值
RCC_DeInit();
//打开外部高速晶振
RCC_HSEConfig(RCC_HSE_ON);
//等待外部高速时钟准备好
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS) //外部高速时钟已经准别好
{
//开启FLASH的预取功能
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
//FLASH延迟2个周期
FLASH_SetLatency(FLASH_Latency_2);
//配置AHB(HCLK)时钟=SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//配置APB2(PCLK2)钟=AHB时钟
RCC_PCLK2Config(RCC_HCLK_Div1);
//配置APB1(PCLK1)钟=AHB 1/2时钟
RCC_PCLK1Config(RCC_HCLK_Div2);
//配置PLL时钟 == 外部高速晶体时钟*9 PLLCLK = 8MHz * 9 = 72 MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
//使能PLL时钟
RCC_PLLCmd(ENABLE);
//等待PLL时钟就绪
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
//配置系统时钟 = PLL时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//检查PLL时钟是否作为系统时钟
while(RCC_GetSYSCLKSource() != 0x08)
{
}
}
}
void NVIC_Configuration(void)
{
#ifdef VECT_TAB_RAM
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
}
void Delay(vu32 nCount)
{
for(; nCount != 0; nCount--);
}
#ifdef DEBUG
void assert_failed(u8* file, u32 line)
{
while (1)
{
}
}
#endif
存储字符、字符串和汉字的头文件没有贴上。