历史上的今天
今天是:2024年09月27日(星期五)
2019年09月27日 | STM32学习之旅⑤ SPI控制TFT,从底层到顶层的设计
2019-09-27 来源:eefocus
一、认识其本质
(一)认识SPI
SPI是串行外设接口(Serial Peripheral Interface)的缩写,SPI 总线是Motorola公司推出的同步串行接口技术,(关于同步还是异步,主要看通信双方的时钟线是否连在一起)。SPI由四根线完成数据传输,分别是SCK(时钟)、MOSI(主出从入)、MISO(主入从出)和SSEL(片选)。通讯时序图如下(当然很多SPI器件的数据手册都会给出响应的时序图):

SPI主要特点有:
可以同时发出和接收串行数据,是一种全双工的通信;
可以当作主机或从机工作
提供频率可编程时钟
发送结束中断标志
写冲突保护
总线竞争保护等
结构简单
传输速度快,通常可以达到几兆到几十兆每秒,常用于数据量比较大的传输。
主要缺点:
需要占用较多的接口线
只能有一个主机
没有应答机制
(二)常见显示器
关于常见的LCD、OLED、TFT
(三)TFT优势
TFT(Thin Film Transistor)是薄膜晶体管的缩写。是有源矩阵液晶的一种。是最好的LCD彩色显示器之一,TFT式显示器具有高响应度、高亮度、高对比度等优点,其显示效果接近CRT式显示器
TFT上的每个液晶像素点都是由集成在像素点后面的薄膜晶体管来驱动,因此反应时间较快,
并且可视角度大,一般可达130度左右
色彩丰富,可支持65536色
回到顶部
二、所需材料
TFT显示屏,看着高大上的电容屏真的买不起。。。
逻辑分析仪,对没错贫穷限制人的想象,不过24M的采样率测个SPI还是勉强能够应付
取模工具, 密码:4h7h
三、底层建筑
(一)模拟SPI
STM32内部外设自带SPI接口,为什么还要用IO口去模拟呢?原因就是移植性,用IO口模拟的程序移植性是最好的,无论是51、msp430还是stm32,或者是DSP,都可以用IO口实现,我们知道经典的51单片机是没有SPI接口的,这时候就需要IO口模拟去实现;在初期写显示驱动程序的时候,IO口模拟出错的可能性比直接用SPI接口小很多,可以大大提高我们写驱动的效率;另外,用IO口模拟SPI还可以让我们对SPI的读写时序有更深刻的认识。
这是TFT手册中对引脚的描述

由设备最短高电平保持时间,可以粗略计算出该设备能够兼容的SPI最高速度为1/(15*2ns)=33MHz,可见SPI是一种高速协议,然而STM32中SPI2的最高速度只支持16MHz,没有能发挥TFT的最高性能

根据以下时序图可以用软件模拟出数据传输的时序,并对TFT进行控制

/****************************************************************************
* 名 称:void SPIv_WriteData(u8 Data)
* 功 能:STM32_模拟SPI写一个字节数据底层函数
* 入口参数:Data
* 出口参数:无
* 说 明:STM32_模拟SPI读写一个字节数据底层函数
****************************************************************************/
void SPIv_WriteData(u8 Data)
{
unsigned char i=0;
for ( i = 8; i > 0; i --)
{
LCD_SCL_CLR;
if ( Data & 0x80)
LCD_SDA_SET; //输出数据
else
LCD_SDA_CLR;
LCD_SCL_SET;
Data <<= 1;
}
}
//******************************************************************
//函数名: LCD_WR_DATA
//功能: 向液晶屏总线写入写8位数据
//输入参数:Data:待写入的数据
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_WR_DATA(u8 data)
{
LCD_CS_CLR; //软件控制片选信号
LCD_RS_SET;
SPIv_WriteData(data);
LCD_CS_SET; //软件控制片选信号
}
//******************************************************************
//函数名: LCD_WR_DATA
//功能: 向液晶屏总线写入写8位数据
//输入参数:Data:待写入的数据
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_WR_DATA(u8 data)
{
LCD_CS_CLR; //软件控制片选信号
LCD_RS_SET;
SPIv_WriteData(data);
LCD_CS_SET; //软件控制片选信号
}
//******************************************************************
//函数名: LCD_WR_REG
//功能: 向液晶屏总线写入写16位指令
//输入参数:Reg:待写入的指令值
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_WR_REG(u16 data)
{
LCD_CS_CLR; //软件控制片选信号
LCD_RS_CLR;
SPIv_WriteData(data);
LCD_CS_SET; //软件控制片选信号
}
调用底层写命令和数据的函数来写寄存器
//******************************************************************
//函数名: LCD_WriteReg
//功能: 写寄存器数据
//输入参数:LCD_Reg:寄存器地址
//LCD_RegValue:要写入的数据
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_WriteReg(u16 LCD_Reg, u16 LCD_RegValue)
{
LCD_WR_REG(LCD_Reg);
LCD_WR_DATA(LCD_RegValue);
}
//******************************************************************
//函数名: LCD_WriteRAM_Prepare
//功能: 开始写GRAM
//在给液晶屏传送RGB数据前,应该发送写GRAM指令
//输入参数:无
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_WriteRAM_Prepare(void)
{
LCD_WR_REG(lcddev.wramcmd);
}
复位函数
//******************************************************************
//函数名: LCD_Reset
//功能: LCD复位函数,液晶初始化前要调用此函数
//输入参数:无
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_RESET(void)
{
#ifdef LCD_RST
LCD_RST_CLR;
#endif
LCD_WR_REG(0x01);
delay_ms(100);
#ifdef LCD_RST
LCD_RST_SET;
#endif
LCD_WR_REG(0x01);
delay_ms(50);
}
初始化GPIO
//******************************************************************
//函数名: LCD_GPIOInit
//功能: 液晶屏IO初始化,液晶初始化前要调用此函数
//输入参数:无
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_GPIOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB ,ENABLE);
#ifdefLCD_RST
GPIO_InitStructure.GPIO_Pin = LCD_LED| LCD_RS| LCD_CS | LCD_SCL | LCD_SDA | LCD_RST;
#else
GPIO_InitStructure.GPIO_Pin = LCD_LED| LCD_RS| LCD_CS | LCD_SCL | LCD_SDA;
#endif
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
初始化TFT
//******************************************************************
//函数名: LCD_Init
//功能: LCD初始化
//输入参数:无
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_Init(void)
{
LCD_GPIOInit();//使用模拟SPI
LCD_RESET(); //液晶屏复位
//************* Start Initial Sequence **********//
//开始初始化液晶屏
LCD_WR_REG(0x11);//Sleep exit
delay_ms (120);
//ST7735R Frame Rate
LCD_WR_REG(0xB1);
LCD_WR_DATA(0x01);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x2D);
LCD_WR_REG(0xB2);
LCD_WR_DATA(0x01);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x2D);
LCD_WR_REG(0xB3);
LCD_WR_DATA(0x01);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x2D);
LCD_WR_DATA(0x01);
LCD_WR_DATA(0x2C);
LCD_WR_DATA(0x2D);
LCD_WR_REG(0xB4); //Column inversion
LCD_WR_DATA(0x07);
//ST7735R Power Sequence
LCD_WR_REG(0xC0);
LCD_WR_DATA(0xA2);
LCD_WR_DATA(0x02);
LCD_WR_DATA(0x84);
LCD_WR_REG(0xC1);
LCD_WR_DATA(0xC5);
LCD_WR_REG(0xC2);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x00);
LCD_WR_REG(0xC3);
LCD_WR_DATA(0x8A);
LCD_WR_DATA(0x2A);
LCD_WR_REG(0xC4);
LCD_WR_DATA(0x8A);
LCD_WR_DATA(0xEE);
LCD_WR_REG(0xC5); //VCOM
LCD_WR_DATA(0x0E);
LCD_WR_REG(0x36); //MX, MY, RGB mode
LCD_WR_DATA(0xC8);
//ST7735R Gamma Sequence
LCD_WR_REG(0xe0);
LCD_WR_DATA(0x0f);
LCD_WR_DATA(0x1a);
LCD_WR_DATA(0x0f);
LCD_WR_DATA(0x18);
LCD_WR_DATA(0x2f);
LCD_WR_DATA(0x28);
LCD_WR_DATA(0x20);
LCD_WR_DATA(0x22);
LCD_WR_DATA(0x1f);
LCD_WR_DATA(0x1b);
LCD_WR_DATA(0x23);
LCD_WR_DATA(0x37);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x07);
LCD_WR_DATA(0x02);
LCD_WR_DATA(0x10);
LCD_WR_REG(0xe1);
LCD_WR_DATA(0x0f);
LCD_WR_DATA(0x1b);
LCD_WR_DATA(0x0f);
LCD_WR_DATA(0x17);
LCD_WR_DATA(0x33);
LCD_WR_DATA(0x2c);
LCD_WR_DATA(0x29);
LCD_WR_DATA(0x2e);
LCD_WR_DATA(0x30);
LCD_WR_DATA(0x30);
LCD_WR_DATA(0x39);
LCD_WR_DATA(0x3f);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x07);
LCD_WR_DATA(0x03);
LCD_WR_DATA(0x10);
LCD_WR_REG(0x2a);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x7f);
LCD_WR_REG(0x2b);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x9f);
LCD_WR_REG(0xF0); //Enable test command
LCD_WR_DATA(0x01);
LCD_WR_REG(0xF6); //Disable ram power save mode
LCD_WR_DATA(0x00);
LCD_WR_REG(0x3A); //65k mode
LCD_WR_DATA(0x05);
LCD_WR_REG(0x29);//Display on
LCD_SetParam();//设置LCD参数
LCD_LED_SET;//点亮背光
}
接下来是关键一步,这一步成功与否决定底层建筑是否搭建成功,在指定位置写入一个像素点
//******************************************************************
//函数名: LCD_DrawPoint
//功能: 在指定位置写入一个像素点数据
//输入参数:(x,y):光标坐标
//返回值: 无
//修改记录:无
//******************************************************************
void LCD_DrawPoint(u16 x,u16 y)
{
LCD_SetCursor(x,y);//设置光标位置
LCD_WR_DATA_16Bit(BLACK);
}
然后再主函数中调用这个函数
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "timer.h"
int main()
{
SystemInit(); //初始化系统,系统时钟设定为72MHz
delay_init(); //配置systick,中断时间设置为72000/72000000 = 1us
史海拾趣
|
FPGA/CPLD 的设计思想与技巧是一个非常大的话题,由于篇幅所限,本文仅介绍一些常用的设计思想与技巧,包括乒乓球操作、串并转换、流水线操作和数据接口的同步方法。希望本文能引起工程师们的注意,如果能有意识地利用这些原则指导日后的设计工作, ...… 查看全部问答> |
|
为了提高广大技术员的摄像机识别能力,特把一些常见的芯片组合列出: 专业红外夜视摄像SONY产品型号及芯片组合 1、A4212SP/G4212SP/D4212SP: 芯片: CXD3141、CXD1267、CXA2096+ICX405; 2、A4512SP/G4512SP/D451 ...… 查看全部问答> |
|
今天考研就结束了,不管结果如何,很多人都可以暂时解脱了。 有个朋友辞了工作专门准备考研,EEWORLD一位同事的男朋友也在潜心考研,真的希望他们付出的能够有收获。… 查看全部问答> |
|
LIBRARY IEEE;USE IEEE.STD_LOGIC_1164.ALL;USE IEEE.STD_LOGIC_ARITH.ALL;USE IEEE.STD_LOGIC_UNSIGNED.ALL;ENTITY dat2bcd ISPORT(CLK:IN STD_LOGIC; DAT:IN STD_LOGIC_VECTOR(7 DOWNTO 0); BCD ...… 查看全部问答> |
|
电路图如上,做的是一个人体检测报警器 有采用菲涅尔透镜,传感器焊在电路板上,透镜盖在上面 现在按照以上的电路及参数做了板子,具体情况如下: 1.有人时,输出重复跳变,每次输出“1”约10秒,输出“0”约0.5秒; 2.没人时,基本跟有人的 ...… 查看全部问答> |
|
基于LM3Sx9xx(With Net)的带RJ45接口的核心模块 本核心模块是基于LM3S6911\\6916\\8962及其引脚兼容芯片而设计,核心电路及网络接口(RJ45)已全部集成到模块之上,并将芯片GPIO及电源引脚引出; 模块采用双侧双排针对外连接而出,各排针间为标准2.54或2.54的整数倍间距,方便嵌入到各类板上应用,以增加 ...… 查看全部问答> |




