STM32F4-DISCO 学习之 SPI 驱动 TFT

之前用过FSMC驱动TFT,实在太浪费IO了,何不用SPI驱动试试.但是,访问不同设备时候,SPI速度不一样,所以,还要掌握速度位的控制.比如慢速器件如LIS302DL,其中SPI的CR1->BR就是分频位,分别从2,4,8,16,32,64,128,256进行分频.而且这个位可以直接用.举个例子:

/* 设置成4分频以便访问LIS302DL */
SPI1->CR1 |= (0x01 << 3);
LIS302DL_Read(Buffer, LIS302DL_OUT_X_ADDR, 6);
X_Offset = Buffer[0];
Y_Offset = Buffer[2];
Z_Offset = Buffer[4];
printf("X = %d,Y = %d,Z = %d
", X_Offset, Y_Offset, Z_Offset);
Delay(500);
/* 设置成2分频以便访问TFT */
SPI1->CR1 |= (0x00 << 3);

TFT SPI也是极其简陋,没写什么太多东西:

void LCD_IO_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin =  LCD_CE | LCD_DC;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIOA->BSRRL = LCD_CE;
}
//DC = 0 => CMD
//DC = 1 => DATA
uint8_t LCD_WriteReg(uint8_t Data)
{
    uint8_t p;
    GPIOA->BSRRH = LCD_DC;
    p = SPI1_SendByte(Data);
    return p;
}
uint8_t LCD_WriteData(uint8_t Data)
{
    uint8_t p;
    GPIOA->BSRRL = LCD_DC;
    p = SPI1_SendByte(Data);
    return p;
}

 但是,其实屏幕不读也可以的,可以直接只写不读.读有时候是为了屏幕ID,分辨批次,这个目前学习,也就偷懒.给一段ILI9341 SPI参考初始化:

void LCD_Init(void)
{
    LCD_WriteReg(0xCF);
    LCD_WriteData(0x00);
    LCD_WriteData(0xD9);
    LCD_WriteData(0X30);
    LCD_WriteReg(0xED);
    LCD_WriteData(0x64);
    LCD_WriteData(0x03);
    LCD_WriteData(0X12);
    LCD_WriteData(0X81);
    LCD_WriteReg(0xE8);
    LCD_WriteData(0x85);
    LCD_WriteData(0x10);
    LCD_WriteData(0x78);
    LCD_WriteReg(0xCB);
    LCD_WriteData(0x39);
    LCD_WriteData(0x2C);
    LCD_WriteData(0x00);
    LCD_WriteData(0x34);
    LCD_WriteData(0x02);
    LCD_WriteReg(0xF7);
    LCD_WriteData(0x20);
    LCD_WriteReg(0xEA);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteReg(0xC0);    //Power control
    LCD_WriteData(0x21);   //VRH[5:0]
    LCD_WriteReg(0xC1);    //Power control
    LCD_WriteData(0x12);   //SAP[2:0];BT[3:0]
    LCD_WriteReg(0xC5);    //VCM control
    LCD_WriteData(0x32);
    LCD_WriteData(0x3C);
    LCD_WriteReg(0xC7);    //VCM control2
    LCD_WriteData(0XC1);
    LCD_WriteReg(0x36);    // Memory Access Control
    LCD_WriteData(0x08);
    LCD_WriteReg(0x3A);
    LCD_WriteData(0x55);
    LCD_WriteReg(0xB1);
    LCD_WriteData(0x00);
    LCD_WriteData(0x18);
    LCD_WriteReg(0xB6);    // Display Function Control
    LCD_WriteData(0x0A);
    LCD_WriteData(0xA2);
    LCD_WriteReg(0xF2);    // 3Gamma Function Disable
    LCD_WriteData(0x00);
    LCD_WriteReg(0x26);    //Gamma curve selected
    LCD_WriteData(0x01);
    LCD_WriteReg(0xE0);    //Set Gamma
    LCD_WriteData(0x0F);
    LCD_WriteData(0x20);
    LCD_WriteData(0x1E);
    LCD_WriteData(0x09);
    LCD_WriteData(0x12);
    LCD_WriteData(0x0B);
    LCD_WriteData(0x50);
    LCD_WriteData(0XBA);
    LCD_WriteData(0x44);
    LCD_WriteData(0x09);
    LCD_WriteData(0x14);
    LCD_WriteData(0x05);
    LCD_WriteData(0x23);
    LCD_WriteData(0x21);
    LCD_WriteData(0x00);
    LCD_WriteReg(0XE1);    //Set Gamma
    LCD_WriteData(0x00);
    LCD_WriteData(0x19);
    LCD_WriteData(0x19);
    LCD_WriteData(0x00);
    LCD_WriteData(0x12);
    LCD_WriteData(0x07);
    LCD_WriteData(0x2D);
    LCD_WriteData(0x28);
    LCD_WriteData(0x3F);
    LCD_WriteData(0x02);
    LCD_WriteData(0x0A);
    LCD_WriteData(0x08);
    LCD_WriteData(0x25);
    LCD_WriteData(0x2D);
    LCD_WriteData(0x0F);
    LCD_WriteReg(0x11);    //Exit Sleep
    Delay(120);
    LCD_WriteReg(0x29);    //Display on
}

然后要写入地址范围寄存器,我做好的函数是LCD_Set_Addr,传入X开始,Y开始,X结束,Y结束.然后清屏也可以实现:

void LCD_Set_Addr(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye)
{
    LCD_WriteReg(0x2a);
    LCD_WriteData(xs >> 8);
    LCD_WriteData(xs);
    LCD_WriteData(xe >> 8);
    LCD_WriteData(xe);
    LCD_WriteReg(0x2b);
    LCD_WriteData(ys >> 8);
    LCD_WriteData(ys);
    LCD_WriteData(ye >> 8);
    LCD_WriteData(ye);
    LCD_WriteReg(0x2C);
}
void LCD_Clear(uint16_t color)
{
    uint8_t VH, VL;
    uint16_t i, j;
    VH = color >> 8;
    VL = color;
    LCD_Set_Addr(0, 0, 239, 319);
    for(i = 0; i < 240; i++)
    {
        for (j = 0; j < 320; j++)
        {
            LCD_WriteData(VH);
            LCD_WriteData(VL);
        }
    }
}

如果这都能实现,画圆,画点,画线,画矩形就不是什么问题了.然而实现字体显示呢?就不是那样子了哦.当然需要取模了.用一个很古老但是很好用的软件,叫PCtoLCD2002,生成方法是这样的.首先输入ASCII表,注意第一个是空格:

QQ截图20150911224723

点击上面的选项,然后如图设置:

QQ截图20150911224941

注意一下宽高就可以生成了哦.

QQ截图20150911225213

主程序:

#include "stm32f4xx.h"
#include "SysTick.h"
#include "DUART.h"
#include "SPI1_Dev.h"
#include "TFT_SPI.h"
int main(void)
{
    /* 打开所有IO端口时钟的脑残大法. */
    RCC->AHB1ENR = 0x001000F7;
    SysTick_Init();
    DUSART_Init();
    SPI1_LowLevel_Init();
    LCD_IO_Init();
    while (1)
    {
        /* 设置成2分频以便访问TFT */
        SPI1_DivChange(SPI_BaudRatePrescaler_4);
        LCD_Init();
        LCD_Clear(0x003F);
        LCD_Fill(100, 100, 200, 200, 0xfe00);
        LCD_DrawLine(10, 50, 70, 60, 0xf3ff);
        LCD_DrawRectangle(70, 70, 110, 250, 0x00ef);
        Draw_Circle(50, 90, 30, 0xffff);
        LCD_ShowNum(90, 90, 10086, 5, 12, 0x0A0A, 0xCCFF);
        LCD_ShowNum(120, 90, 10086, 5, 16, 0x0A0A, 0xBBFF);
        LCD_ShowNum(160, 90, 10086, 5, 24, 0x0A0A, 0xAAFF);
        LCD_ShowString(90, 130, 60, 12, 12, "Tater", 0xFFCC, 0xAE00);
        LCD_ShowString(90, 170, 80, 16, 16, "Tater", 0xFFCC, 0xAEFF);
        LCD_ShowString(90, 210, 100, 16, 24, "Tater", 0x00FE, 0xFFAE);
        while(1)
        {
        }
    }
}

屏幕质量不好见笑了:

QQ截图20150911231812

程序下载(接线见TFT头文件):

 SPI驱动TFT

附上取模工具:

PCtoLCD2002完美版

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注