FMC驱动8位并口TFT
主控:STM32L476VET6
屏幕:8位并口 ILI9163S 128*160
Stm32cube配置如下
1.FMC配置
extended mode(读写使用不同的时序)关闭
AddressSetupTime
此参数用于设置地址建立时间,单位FMC时钟周期个数,范围0 -15
DataSetupTime
此参数用于设置数据建立时间,单位FMC时钟周期个数,范围1 -255。
BusTurnAroundDuration
此参数用于设置总线TurnAround(总线周转阶段)持续时间,单位FMC时钟周期个数,范围0 -15。
2.GPIO配置
配置LCD的RST和LED背光控制引脚
3.屏幕电路
FSMC 读写GRAM函数
不同的地址线要计算地址偏移量
/* 当选择NE1 连接 LCD时, 地址范围: 0x60000000~0x63FFFFFF
* 当选用FSMC_A16地址线时
* 16位数据时: 16bits => FSMC[24:0]== HADDR[25:1]
8位数据时 : 8bits => FSMC[25:0]== HADDR[25:0]
* register base address: 0x60000000
* 16位数据时RAM base address: 0x60020000 = 0x60000000 + 2^16 * 2 = 0x60020000
* 8 位数据时RAM base address: 0x60010000 = 0x60000000 + 2^16 = 0x60010000
* 选用不同的地址线要重新计算偏移地址
8位并口屏,A16地址线,所以数据地址起始为0x60010000
*/
#define Bank1_LCD_DATA ((uint32_t)0x60010000) /* display data address */
#define Bank1_LCD_REG ((uint32_t)0x60000000) /* display register address */
/* LCD write data and register */
#define LCD_WR_DATA(value) ((*(__IO uint8_t*)(Bank1_LCD_DATA)) = ((uint8_t)(value))) //写数据寄存器
#define LCD_WR_REG(index) ((*(__IO uint8_t*)(Bank1_LCD_REG)) = ((uint8_t)index)) //写命令寄存器
屏幕初始化
//ILI9163初始化
void ILI9163_Init(void)
{
HAL_GPIO_WritePin(GPIOB, LCD_RST_Pin,GPIO_PIN_SET);
delayms(1);
HAL_GPIO_WritePin(GPIOB, LCD_RST_Pin,GPIO_PIN_RESET);
delayms(10);
HAL_GPIO_WritePin(GPIOB, LCD_RST_Pin,GPIO_PIN_SET);
delayms(100);
LCD_WR_REG(0x11); //退出睡眠模式
HAL_Delay(120);
LCD_WR_REG(0x26); //设置伽玛曲线
LCD_WR_DATA(0x04);
LCD_WR_REG(0xB1);//设置帧速率
LCD_WR_DATA(0x08);
LCD_WR_DATA(0x14);
LCD_WR_REG(0xB8); //背光控制(灰度)
LCD_WR_DATA(0x01);
LCD_WR_REG(0xC0); //功耗设置
LCD_WR_DATA(0x08);
LCD_WR_DATA(0x02);
LCD_WR_REG(0xC1); //功耗设置2
LCD_WR_DATA(0x05);
LCD_WR_REG(0xC5); //设置VCOMH/VCOML 电压
LCD_WR_DATA(0x3A); //0X46
LCD_WR_DATA(0x32); //0X40
LCD_WR_REG(0xC7); // Set VMF
LCD_WR_DATA(0xbe); // 0XC0
LCD_WR_REG(0x3a); //16bit/像素
LCD_WR_DATA(0x05);
LCD_WR_REG(0x2A); //设置X范围
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x7F);
LCD_WR_REG(0x2B); //设置Y范围
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x9F);
LCD_WR_REG(0xB4); //显示反转控制
LCD_WR_DATA(0x00);
LCD_WR_REG(0xB7); //入口模式
LCD_WR_DATA(0x01);
LCD_WR_REG(0xB8); //背光控制
LCD_WR_DATA(0x00);
LCD_WR_REG(0xf2); //使能3伽玛控制
LCD_WR_DATA(0x01);
LCD_WR_REG(0x36); //设置扫描方向
LCD_WR_DATA(0xC0);
LCD_WR_REG(0xE0); //正极校验伽玛
LCD_WR_DATA(0x3F);//p1
LCD_WR_DATA(0x26);//p2
LCD_WR_DATA(0x23);//p3
LCD_WR_DATA(0x30);//p4
LCD_WR_DATA(0x28);//p5
LCD_WR_DATA(0x10);//p6
LCD_WR_DATA(0x55);//p7
LCD_WR_DATA(0xB7);//p8
LCD_WR_DATA(0x40);//p9
LCD_WR_DATA(0x19);//p10
LCD_WR_DATA(0x10);//p11
LCD_WR_DATA(0x1E);//p12
LCD_WR_DATA(0x02);//p13
LCD_WR_DATA(0x01);//p14
LCD_WR_DATA(0x00);//p15
LCD_WR_REG(0xE1); //负极校验伽玛
LCD_WR_DATA(0x00);//p1
LCD_WR_DATA(0x19);//p2
LCD_WR_DATA(0x1C);//p3
LCD_WR_DATA(0x0F);//p4
LCD_WR_DATA(0x14);//p5
LCD_WR_DATA(0x0F);//p6
LCD_WR_DATA(0x2A);//p7
LCD_WR_DATA(0x48);//p8
LCD_WR_DATA(0x3F);//p9
LCD_WR_DATA(0x06);//p10
LCD_WR_DATA(0x1D);//p11
LCD_WR_DATA(0x21);//p12
LCD_WR_DATA(0x3d);//p13
LCD_WR_DATA(0x3e);//p14
LCD_WR_DATA(0x3f);//p15
LCD_WR_REG(0x29); // 开启显示
}
设置窗口范围
//设置窗口范围
//X,Y 为起点坐标;width,height为窗口长宽
static void LCD_OpenWindow(uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
LCD_WR_REG(0x2A); //设置X范围坐标
LCD_WR_DATA(x>>8); //起始点高8位
LCD_WR_DATA(x&0x00FF); //起始点低8位
LCD_WR_DATA((x+width-1)>>8); //结束点高8位
LCD_WR_DATA((x+width-1)&0x00FF); //结束点低8位
LCD_WR_REG(0X2B); //设置Y范围坐标
LCD_WR_DATA(y>>8); //起始点高8位
LCD_WR_DATA(y&0x00FF); //起始点低8位
/* pate end */
LCD_WR_DATA((y+height-1)>>8); //结束点高8位
LCD_WR_DATA((y+height-1)&0x00FF); //结束点低8位
LCD_WR_REG(0x2C); //开始写入GRAM
}
画图函数
//画一个点
void LCD_DrawPoint(uint16_t x, uint16_t y, uint16_t color)
{
LCD_OpenWindow(y, x, 1, 1); //设置坐标
LCD_WR_DATA(color>>8); //颜色高8位
LCD_WR_DATA(color&0xFF); //颜色低8位
}
//画一条线
void LCD_DrawLine(uint16_t start_x, uint16_t start_y, uint16_t end_x, uint16_t end_y, uint16_t color)
{
uint16_t x = 0, y = 0, k = 0;
if((start_x == end_x) && (start_y == end_y)) //如果是一个点
{
LCD_DrawPoint(start_x, start_y, color);
}
else if(abs(end_y - start_y) > abs(end_x - start_x))
{
if(start_y > end_y)
{
k = start_y;
start_y = end_y;
end_y = k;
k = start_x;
start_x = end_x;
end_x = k;
}
for(y = start_y; y < end_y; ++y)
{
x = (uint16_t)(y - start_y)*(end_x - start_x) / (end_y - start_y) + start_x;
LCD_DrawPoint(x, y, color);
}
}
else
{
if(start_x > end_x)
{
k = start_y;
start_y = end_y;
end_y = k;
k = start_x;
start_x = end_x;
end_x = k;
}
for(x = start_x; x < end_x; ++x)
{
y = (uint16_t)(x - start_x)*(end_y - start_y)/ (end_x - start_x) + start_y;
LCD_DrawPoint(x, y, color);
}
}
}
//画矩形
void LCD_DrawRectangle(uint16_t start_x, uint16_t start_y, uint16_t end_x, uint16_t end_y, uint16_t color)
{
/*
top-left coordinate(start_x, start_y), bottom-right coordinate(end_x, end_y)
'----------------'
' '
' '
' '
' '
'----------------'
*/
LCD_DrawLine(start_x, start_y, start_x, end_y, color);
LCD_DrawLine(start_x, start_y, end_x, start_y, color);
LCD_DrawLine(end_x, start_y, end_x, end_y, color);
LCD_DrawLine(start_x, end_y, end_x, end_y, color);
}
//画圆,圆心,半径,颜色
void LCD_DrawCircle(uint16_t x, uint16_t y, uint16_t radius, uint16_t color)
{
uint16_t a,b;
int16_t di;
a = 0;
b = radius;
di = 3 - (radius << 1);
while(a <= b)
{
LCD_DrawPoint(x - b, y - a, color); //3
LCD_DrawPoint(x + b, y - a, color); //0
LCD_DrawPoint(x - a, y + b, color); //1
LCD_DrawPoint(x - a, y - b, color); //2
LCD_DrawPoint(x + b, y + a, color); //4
LCD_DrawPoint(x + a, y - b, color); //5
LCD_DrawPoint(x + a, y + b, color); //6
LCD_DrawPoint(x - b, y + a, color);
a++;
/* Bresenham algorithm */
if(di < 0)
{
di += 4*a + 6;
}
else
{
di += 10 + 4 * (a - b);
b--;
}
LCD_DrawPoint(x + a, y + b, color);
}
}
//填充整个屏幕
void LCD_FillScreen(uint16_t color)
{
LCD_FillRectangle(0, 0, 160, 128, color);
}
//画一个ASCII字符:坐标,字符编号,字符颜色,背景色,字符大小
void LCD_DispASCIICharacter(uint16_t x, uint16_t y, uint8_t character, uint16_t fColor, uint16_t bColor, uint8_t font)
{
uint32_t i = 0, j = 0, k = 0;
for(k = 0; k < sizeof(codeASCII_24) / sizeof(codeASCII_24[0]); ++k) //按编码搜索字符
{
if(codeASCII_24[k].Index == character)
{
for(i = 0; i < 16; ++i)
{
uint8_t m = codeASCII_24[k].Msk[i];
for(j = 0; j < 8; j++)
{
if(m & 0x80)
{
LCD_DrawPoint(x+j,y+i,fColor);
}
else
{
LCD_DrawPoint(x+j,y+i,bColor);
}
m <<= 1;
}
}
}
}
}
//画一个汉字:坐标,字符编号,字符颜色,背景色,字符大小
void LCD_DispHZCharacter(uint16_t x, uint16_t y, uint8_t index[2], uint16_t fColor, uint16_t bColor, uint8_t font)
{
uint16_t width = 0, height = 0;
uint32_t i = 0, j = 0, k = 0;
uint8_t m ;
uint8_t a=0 ;
width = GetFontWidth(font, HZ_CHARACTER);
height = GetFontHeight(font, HZ_CHARACTER);
LCD_OpenWindow(x, y, width, height);
switch(font)
{
case FONT_SIZE_24:
for(k = 0; k < sizeof(codeHZ_24) / sizeof(codeHZ_24[0]); ++k)
{
if((codeHZ_24[k].Index[0] == index[0]) && (codeHZ_24[k].Index[1] == index[1]))
{
for(i = 0; i < 72; ++i)
{
m= codeHZ_24[k].Msk[i];
if((i%3==0)&&(i!=0))
{
y++;
a=0;
}
for(j = 0; j < 8; ++j)
{
if(m & 0x80)
{
// LCD_DrawPoint(x+a+j,y,fColor);
LCD_WR_DATA(fColor>>8); //颜色高8位
LCD_WR_DATA(fColor&0xFF); //颜色低8位
}
else
{
// LCD_DrawPoint(x+a+j,y,bColor);
LCD_WR_DATA(bColor>>8); //颜色高8位
LCD_WR_DATA(bColor&0xFF); //颜色低8位
}
m <<= 1;
}
a=a+8;
}
break;
}
}
break;
default:
break;
}
}
//显示图片1
//width,height 图片长宽
void LCD_DispPicture1(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t* str)
{
LCD_OpenWindow(y, x,height, width);
uint32_t i,j;
j = (width )*(height);
for(i=0;i<j;i++)
{
LCD_WR_DATA(str[2*i+1]);
LCD_WR_DATA(str[2*i]);
}
}
写数据后地址会自动+1,不用再设置坐标。窗口范围要设置正确,否则会出现乱码
0X36可以设置扫描方向
这是存储访问控制指令,可以控制 ILI9341 存储器的读写方向,简单的说,就是在连续写 GRAM 的时候,可以控制 GRAM 指针的增长方向,从而控制显示方式。(读 GRAM 也是一样)。该指令如表:
0X36 指令后面,紧跟一个参数,主要关注: MY、 MX、 MV 这三个位,通过这三个位的设置,可以控制整个 ILI9341 的全部扫描方向。