之前用过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表,注意第一个是空格:
点击上面的选项,然后如图设置:
注意一下宽高就可以生成了哦.
主程序:
#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) { } } }
屏幕质量不好见笑了:
程序下载(接线见TFT头文件):
附上取模工具: