1.硬件介绍
本期介绍一下智能家居项目中用到的TFT显示屏。此模块在项目中用于温湿度、光度、陀螺仪角度等数据的显示。


2.SPI协议
SPI(Serial Peripheral Interface,串行外设接口) 是一种高速的、全双工、同步的串行通信协议,主要用于单片机与各种外设之间进行数据传输,下面为你详细介绍:
2.1.基本组成
SPI 通信通常由主设备(Master)和从设备(Slave)组成 ,一个主设备可以连接多个从设备。SPI 接口一般使用 4 根线进行通信:
时钟线(SCK,Serial Clock)
由主设备产生,用于同步主设备和从设备之间的数据传输,就像大家一起跟着节拍器打拍子一样,保证数据传输节奏一致。
主机输出 / 从机输入线(MOSI,Master Output, Slave Input)
主设备通过这条线向从设备发送数据。比如主设备是单片机,从设备是显示屏,单片机要给显示屏发送显示内容的数据,就通过 MOSI 线传输。
主机输入 / 从机输出线(MISO,Master Input, Slave Output)
从设备通过这条线向主设备发送数据。比如从设备是温度传感器,要把采集到的温度数据发送给主设备单片机,就经由 MISO 线。
从机选择线(SS,Slave Select,有的也叫 CS,Chip Select )
由主设备控制,用来选择与哪个从设备进行通信。因为一个主设备可能连接多个从设备,当主设备要和某个从设备通讯时,就把对应从设备的 SS 线拉低,相当于告诉这个从设备 “该你干活啦” ,其他从设备的 SS 线保持高电平,处于非工作状态。

2.2.通信过程
选择从设备
主设备先将目标从设备的 SS 线拉低,选中要通信的从设备。比如主设备要和连接在 SPI 总线上的 Flash 存储芯片通讯,就把 Flash 芯片对应的 SS 线拉低。
时钟同步
主设备通过 SCK 线产生时钟信号,确定数据传输的节拍。数据在时钟信号的上升沿或者下降沿进行传输,具体是上升沿还是下降沿传输,由主设备和从设备事先约定好的 SPI 模式决定(SPI 有 4 种工作模式 ,主要区别在于时钟极性 CPOL 和时钟相位 CPHA 的不同组合)。
数据传输
在时钟信号的驱动下,主设备通过 MOSI 线发送数据给从设备,从设备通过 MISO 线发送数据给主设备,两者的数据传输是同时进行的,这就是全双工的体现。
比如主设备给从设备发送一个控制指令,同时从设备把自己的状态数据反馈给主设备。数据通常以字节(8 位)为单位进行传输 ,主设备和从设备在每一个时钟周期内,各自发送和接收 1 位数据,经过 8 个时钟周期完成一个字节的传输(数据传输可以高位先行MSB或者低位先行LSB)。
结束通信
主设备将从设备的 SS 线拉高,结束与该从设备的通信 。此时,从设备进入等待状态,直到下一次被主设备选中。
2.3.CPOL 和 CPHA 的作用
SPI 通信中,主从设备通过时钟线(SCL) 同步数据传输,而CPOL 和 CPHA 共同决定了:
- 时钟线在空闲时的电平(CPOL);
- 数据在时钟的哪个边沿被采样(CPHA)。
两者组合形成 SPI 的 4 种工作模式(模式 0~3),理解这两个参数是掌握 SPI 工作模式的基础。
2.3.1.CPOL(时钟极性):定义时钟线的空闲电平
- 定义:当 SPI 没有数据传输(空闲状态)时,时钟线(SCL)的电平状态。
- 若空闲时 SCL 为高电平,则 CPOL=1;
- 若空闲时 SCL 为低电平,则 CPOL=0。
- 举例:比如一个 SPI 设备设定 CPOL=1,那么在不传输数据时,SCL 线一直保持高电平,直到开始传输时才会跳变。
2.3.2.CPHA(时钟相位):定义数据的采样时刻
核心:数据在时钟的 “第几个边沿” 被采样(采集),分为两种情况:
CPHA=0:在 “第 1 个边沿”(奇数边沿)采样
- 时钟从 “空闲状态” 开始跳变的第一个边沿(上升沿或下降沿)就是采样时刻。
- 边沿极性由 CPOL 决定:
- 若 CPOL=0(空闲低电平),第一个边沿是上升沿(低→高),此时采样;
- 若 CPOL=1(空闲高电平),第一个边沿是下降沿(高→低),此时采样。
CPHA=1:在 “第 2 个边沿”(偶数边沿)采样
- 数据采样发生在时钟的第二个边沿(即偶数次跳变),此时的边沿极性与 CPHA=0 相反:
- 若 CPOL=0(空闲低电平):第一个边沿是上升沿(低→高,奇数),第二个边沿是下降沿(高→低,偶数),此时采样;
- 若 CPOL=1(空闲高电平):第一个边沿是下降沿(高→低,奇数),第二个边沿是上升沿(低→高,偶数),此时采样。
2.4.SPI的4种工作模式
参数组合
|
空闲电平(CPOL)
|
采样边沿(CPHA)
|
对应的边沿极性示例
|
CPOL=0, CPHA=0
|
低电平
|
第 1 个边沿(上升沿)
|
低→高时采样
|
CPOL=0, CPHA=1
|
低电平
|
第 2 个边沿(下降沿)
|
高→低时采样
|
CPOL=1, CPHA=0
|
高电平
|
第 1 个边沿(下降沿)
|
高→低时采样
|
CPOL=1, CPHA=1
|
高电平
|
第 2 个边沿(上升沿)
|
低→高时采样
|

CPHA=0时,CPOL=1与CPOL=0时的时序

CPHA=1时,CPOL=1与CPOL=0时的时序

3.软件设计
我们通过GPIO模拟SPI协议来对显示屏进行控制。
3.1.STM32Cubemx配置
SDA对应MOSI,DC引脚用于切换命令或者数据模式(0/1),SCK引脚为时钟引脚,CS引脚片选引脚,RES为复位引脚,都配置为推挽输出即可。


3.2.SPI驱动代码实现
3.2.1.SPI写一个字节
这段代码实现的是SPI 模式 0(CPOL=0, CPHA=0),是显示屏控制中最常见的模式之一。数据传输为MSB高位先行。
void LCD_Writ_Bus(char dat) //串行数据写入
{
u8 i;
for(i=0;i<8;i++)
{
OLED_SCLK_Clr();
if(dat&0x80)
OLED_SDIN_Set();
else
OLED_SDIN_Clr();
OLED_SCLK_Set();
dat<<=1;
}
}
3.2.2.写寄存器
DC引脚低电平,代表写寄存器。
void LCD_WR_REG(char da)
{
OLED_CS_Clr();
OLED_DC_Clr();
LCD_Writ_Bus(da);
OLED_CS_Set();
}
3.2.3.写数据
DC引脚高电平,代表写数据。
void LCD_WR_DATA8(char da) //发送数据-8位参数
{
OLED_CS_Clr();
OLED_DC_Set();
LCD_Writ_Bus(da);
OLED_CS_Set();
}
3.2.4.显示屏初始化
void Lcd_Init(void)
{
HAL_GPIO_WritePin(GPIOA, TFT_DC_Pin|TFT_SCK_Pin|TFT_RES_Pin
|TFT_SDA_Pin, GPIO_PIN_SET);
OLED_RST_Clr();
delay_ms(20);
OLED_RST_Set();
delay_ms(20);
OLED_BLK_Set();
//************* Start Initial Sequence **********//
LCD_WR_REG(0x36);
LCD_WR_DATA8(0x00);
LCD_WR_REG(0x3A);
LCD_WR_DATA8(0x05);
LCD_WR_REG(0xB2);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x33);
LCD_WR_DATA8(0x33);
LCD_WR_REG(0xB7);
LCD_WR_DATA8(0x35);
LCD_WR_REG(0xBB);
LCD_WR_DATA8(0x19);
LCD_WR_REG(0xC0);
LCD_WR_DATA8(0x2C);
LCD_WR_REG(0xC2);
LCD_WR_DATA8(0x01);
LCD_WR_REG(0xC3);
LCD_WR_DATA8(0x12);
LCD_WR_REG(0xC4);
LCD_WR_DATA8(0x20);
LCD_WR_REG(0xC6);
LCD_WR_DATA8(0x0F);
LCD_WR_REG(0xD0);
LCD_WR_DATA8(0xA4);
LCD_WR_DATA8(0xA1);
LCD_WR_REG(0xE0);
LCD_WR_DATA8(0xD0);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x0D);
LCD_WR_DATA8(0x11);
LCD_WR_DATA8(0x13);
LCD_WR_DATA8(0x2B);
LCD_WR_DATA8(0x3F);
LCD_WR_DATA8(0x54);
LCD_WR_DATA8(0x4C);
LCD_WR_DATA8(0x18);
LCD_WR_DATA8(0x0D);
LCD_WR_DATA8(0x0B);
LCD_WR_DATA8(0x1F);
LCD_WR_DATA8(0x23);
LCD_WR_REG(0xE1);
LCD_WR_DATA8(0xD0);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x0C);
LCD_WR_DATA8(0x11);
LCD_WR_DATA8(0x13);
LCD_WR_DATA8(0x2C);
LCD_WR_DATA8(0x3F);
LCD_WR_DATA8(0x44);
LCD_WR_DATA8(0x51);
LCD_WR_DATA8(0x2F);
LCD_WR_DATA8(0x1F);
LCD_WR_DATA8(0x1F);
LCD_WR_DATA8(0x20);
LCD_WR_DATA8(0x23);
LCD_WR_REG(0x21);
LCD_WR_REG(0x11);
LCD_WR_REG(0x29);
LCD_Clear(WHITE);
}
3.2.5.main函数
int main()
{
Lcd_Init();
LCD_Clear(WHITE); //清屏
BACK_COLOR=WHITE;
POINT_COLOR=BLACK;
Set_Myhanzi(70, 10, "chuanganqi");
Set_Myhanzi(20, 55, "guangdu");
Set_Myhanzi(20, 105, "wendu");
Set_Myhanzi(20, 155, "shidu");
while(1){}
}
4.完整代码

我用夸克网盘给你分享了「SmartHom...iyun」,点击链接或复制整段内容,打开「夸克APP」即可获取。
/~1d17370YmM~:/
链接:夸克网盘分享
提取码:1KdH