4.SPI外设—LCD小案例
SPI(Serial Peripheral Interface)是一种常见的同步串行通信协议,用于微控制器和各种外围设备之间的通信。SPI通信通常涉及至少四根线:SCLK(时钟线)、MOSI(主设备数据输出线)、MISO(主设备数据输入线)和CS(从设备选择线)。在与LCD屏幕的通信中,SPI可以用来发送命令和数据。
SPI 知识点
- SPI 模式:SPI有四种工作模式,由CPOL和CPHA两个参数决定,它们影响时钟的空闲状态和数据的采样时刻。
- 时钟频率:SPI通信的时钟频率可以根据需要设置,但要受到外围设备的限制。
- 数据传输:SPI支持全双工通信,可以同时进行数据的发送和接收。
- 片选信号:CS信号用于激活特定的SPI设备,低电平有效。
SPI连接LCD案例
以下是一个使用STM32标准外设库连接SPI LCD的详细案例:
-
硬件连接:
- LCD VCC 连接 STM32 DC5V/3.3V(电源)
- LCD GND 连接 STM32 GND(电源地)
- LCD SDI(MOSI) 连接 STM32 PB15(液晶屏SPI总线数据写信号)
- LCD SDO(MISO) 连接 STM32 PB14(液晶屏SPI总线数据读信号,如果不使用可以不接线)
- LCD LED 连接 STM32 PB9(液晶屏背光控制信号,如果不控制可以接5V或3.3V)
- LCD SCK 连接 STM32 PB13(液晶屏SPI总线时钟信号)
- LCD DC/RS 连接 STM32 PB10(液晶屏数据/命令控制信号)
- LCD RST 连接 STM32 PB12(液晶屏复位控制信号)
- LCD CS 连接 STM32 PB11(液晶屏片选控制信号)
-
LCD小案例:
spi.h
// spi.h
#ifndef __SPI_H
#define __SPI_H
#include "stm32f10x.h"
void SPI2_Init(void);
#endif // __SPI_H
spi.c
// spi.c
#include "spi.h"
void SPI2_Init(void) {
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 使能SPI2和GPIOB的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
// 配置SPI2的SCK和MOSI管脚为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 配置SPI2的MISO管脚为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// SPI2配置
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);
// 使能SPI2
SPI_Cmd(SPI2, ENABLE);
}
lcd.h
// lcd.h
#ifndef __LCD_H
#define __LCD_H
#include "stm32f10x.h"
void LCD_Init(void);
void LCD_SendByte(uint8_t data, uint8_t command);
void LCD_SetCursor(uint8_t x, uint8_t y);
void LCD_WriteString(char *str);
#endif // __LCD_H
lcd.c
// lcd.c
#include "lcd.h"
#include "spi.h"
// 假设DC和CS管脚已经连接到GPIOB的Pin_10和Pin_11
#define LCD_DC_PORT GPIOB
#define LCD_DC_PIN GPIO_Pin_10
#define LCD_CS_PORT GPIOB
#define LCD_CS_PIN GPIO_Pin_11
// 假设LCD的宽度和高度
#define LCD_WIDTH 128
#define LCD_HEIGHT 64
// 假设LCD的命令集
#define LCD_CMD_DISPLAY_ON 0xAF
#define LCD_CMD_CLEAR 0x5C
// 延时函数
void Delay(uint32_t delay_time) {
// 简单的延时函数,用于LCD初始化
volatile uint32_t i;
for (i = 0; i < delay_time * 1000; i++);
}
void LCD_Init(void) {
// 初始化LCD显示参数
GPIO_WriteBit(LCD_CS_PORT, LCD_CS_PIN, Bit_SET); // CS高电平,不选中LCD
GPIO_WriteBit(LCD_DC_PORT, LCD_DC_PIN, Bit_SET); // DC高电平,命令模式
// 延时,等待LCD准备好
Delay(100);
// 发送LCD初始化命令序列
LCD_SendByte(0xAE, 1); // 关闭显示
LCD_SendByte(0xD5, 1); // 设置显示时钟频率
LCD_SendByte(0x50, 1); // 推荐频率
LCD_SendByte(0xA8, 1); // 设置复用率
LCD_SendByte(0x3F, 1); // 推荐复用率
LCD_SendByte(0xD3, 1); // 设置显示偏移
LCD_SendByte(0x00, 1); // 无偏移
LCD_SendByte(0x40, 1); // 显示起始行
LCD_SendByte(0x8D, 1); // 电荷泵设置
LCD_SendByte(0x14, 1); // 打开电荷泵
LCD_SendByte(0x20, 1); // 设置内存模式
LCD_SendByte(0x0C, 1); // 打开通路
LCD_SendByte(LCD_CMD_DISPLAY_ON, 1); // 打开显示
// 清除LCD显示
LCD_SendByte(LCD_CMD_CLEAR, 1);
Delay(100);
}
void LCD_SendByte(uint8_t data, uint8_t command) {
// 设置DC和CS管脚的状态
if (command) {
// 发送命令
GPIO_WriteBit(LCD_DC_PORT, LCD_DC_PIN, Bit_SET); // DC高电平,命令模式
} else {
// 发送数据
GPIO_WriteBit(LCD_DC_PORT, LCD_DC_PIN, Bit_RESET); // DC低电平,数据模式
}
GPIO_WriteBit(LCD_CS_PORT, LCD_CS_PIN, Bit_RESET); // CS低电平,选中LCD
// 发送数据到SPI
SPI_I2S_SendData(SPI2, data);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); // 等待发送完毕
// 取消选中LCD
GPIO_WriteBit(LCD_CS_PORT, LCD_CS_PIN, Bit_SET);
}
void LCD_SetCursor(uint8_t x, uint8_t y) {
// 设置LCD的光标位置
LCD_SendByte(0x00, 1); // 设置地址模式
LCD_SendByte((x >> 4) & 0x0F, 0); // 设置列地址的高两位
LCD_SendByte(x & 0x0F, 0); // 设置列地址的低两位
LCD_SendByte(0x10, 1); // 设置页地址
LCD_SendByte((y >> 4) & 0x0F, 0); // 设置页地址的高两位
LCD_SendByte(y & 0x0F, 0); // 设置页地址的低两位
}
void LCD_WriteString(char *str) {
// 写字符串到LCD
while (*str) {
LCD_SendByte(*str++, 0); // 0表示数据模式
}
}
main.c
// main.c
#include "stm32f10x.h"
#include "spi.h"
#include "lcd.h"
int main(void) {
// 初始化系统
SystemInit();
// 初始化SPI2
SPI2_Init();
// 初始化LCD
LCD_Init();
// 设置光标位置
LCD_SetCursor(0, 0);
// 显示字符串"你好"
LCD_WriteString("你好");
while (1) {
// 主循环
}
}
说明!!!!
- spi.c 和 spi.h:负责SPI接口的初始化和配置。
- lcd.c 和 lcd.h:负责LCD的初始化、设置光标位置和写字符串。
- main.c:主函数,初始化系统、SPI和LCD,然后显示字符串"你好"。
请注意,这个示例代码假设了LCD的命令集和参数,实际使用时需要根据具体的LCD型号和数据手册进行调整。例如,不同的LCD可能有不同的初始化命令序列、地址设置方式和显示参数。