之前学习了如何使用LCD,记录一下,关于spi部分是从野火的资料中截取,也会加入我自己的备注,便于理解,代码部分在后面,请耐心看完。
一.SPI协议简介
SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间,要求通讯速率较高的场合。
SPI通讯系统
SPI 通讯使用 3 条总线及片选线,3 条总线分别为 SCK、MOSI、MISO,片选线为 SS ,它们的作 用介绍如下: (1) SS( Slave Select) 从设备选择信号线,常称为片选信号线,也称为 NSS、CS,以下用 NSS表示。当有多个 SPI 从设备与 SPI 主机相连时,设备的其它信号线 SCK、MOSI 及 MISO同时并联到相同的 SPI 总线上,即无论有多少个从设备,都共同只使用这 3 条总线;而每个从设备都有独立的这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。所以 SPI 通讯以 NSS 线置低电平为开始信号,以NSS 线被拉高作为结束信号。(在LCD中,片选线有很多名称,CS,SS,NSS都是指片选)
(2) SCK (Serial Clock): 时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样,如 STM32 的 SPI 时钟频率最大为 fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。 (3) MOSI (Master Output,Slave Input): 主设备输出/从设备输入引脚。主机的数据从这条信号线 输出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。 (与IIC相比,这个就是信号线,由主机向从机发送数据,即SDA) (4) MISO(Master Input,,Slave Output): 主设备输入/从设备输出引脚。主机从这条信线读入数据, 从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。 (从机向主机发送数据,使用触摸屏时需要这根线)。
SPI时序图
CS 信号线由高变低,是 SPI 通讯的起始信号,CS 信号由低变高,是 SPI 通讯的停止信号。SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据。
二.LCD
数据线,时钟线,片选线都有,分别对应SDA,SCL,CS,还有两根线是DC,数据命令选择;RES液晶复位;BL,背光选择。在淘宝商家处都有详细资料。
三.代码
我的代码是移植淘宝卖家发的资料,接下来说一下要改的部分。 1.在cubemx 中对引脚的配置如下,可以自己选择空闲的引脚,配置为output。 2.将代码拆分为通讯时序和显示函数两个部分。
#include "SPI_LCD.h"
#include "main.h"
#include "string.h"
//管理LCD重要参数
//默认为竖屏
_lcd_dev lcddev;
//画笔颜色,背景颜色
u16 POINT_COLOR = 0x0000,BACK_COLOR = 0xFFFF;
u16 DeviceCode;
/*SPI写数据*/
void SPIv_WriteData(u8 Data)
{
unsigned char i=0;
for(i=8;i>0;i--)
{
if(Data&0x80)
{
SPI_MOSI_SET; //输出数据
}
else
{
SPI_MOSI_CLR;
}
SPI_SCLK_CLR;
SPI_SCLK_SET;
Data<<=1;
}
}
/*****************************************************************************
* @name :void LCD_WR_REG(u8 data)
* @date :2018-08-09
* @function :Write an 8-bit command to the LCD screen
* @parameters :data:Command value to be written
* @retvalue :None
******************************************************************************/
void LCD_WR_REG(u8 data)
{
LCD_CS_CLR;
LCD_RS_CLR;
SPIv_WriteData(data);
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_WR_DATA(u8 data)
* @date :2018-08-09
* @function :Write an 8-bit data to the LCD screen
* @parameters :data:data value to be written
* @retvalue :None
******************************************************************************/
void LCD_WR_DATA(u8 data)
{
LCD_CS_CLR;
LCD_RS_SET;
SPIv_WriteData(data);
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)
* @date :2018-08-09
* @function :Write data into registers
* @parameters :LCD_Reg:Register address
LCD_RegValue:Data to be written
* @retvalue :None
******************************************************************************/
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)
{
LCD_WR_REG(LCD_Reg);
LCD_WR_DATA(LCD_RegValue);
}
/*****************************************************************************
* @name :void LCD_WriteRAM_Prepare(void)
* @date :2018-08-09
* @function :Write GRAM
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_WriteRAM_Prepare(void)
{
LCD_WR_REG(lcddev.wramcmd);
}
/*****************************************************************************
* @name :void Lcd_WriteData_16Bit(u16 Data)
* @date :2018-08-09
* @function :Write an 16-bit command to the LCD screen
* @parameters :Data:Data to be written
* @retvalue :None
******************************************************************************/
void Lcd_WriteData_16Bit(u16 Data)
{
LCD_CS_CLR;
LCD_RS_SET;
SPIv_WriteData(Data>>8);
SPIv_WriteData(Data);
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_DrawPoint(u16 x,u16 y)
* @date :2018-08-09
* @function :Write a pixel data at a specified location
* @parameters :x:the x coordinate of the pixel
y:the y coordinate of the pixel
* @retvalue :None
******************************************************************************/
void LCD_DrawPoint(u16 x,u16 y)
{
LCD_SetCursor(x,y);//设置光标位置
Lcd_WriteData_16Bit(POINT_COLOR);
}
/*****************************************************************************
* @name :void LCD_Clear(u16 Color)
* @date :2018-08-09
* @function :Full screen filled LCD screen
* @parameters :color:Filled color
* @retvalue :None
******************************************************************************/
void LCD_Clear(u16 Color)
{
unsigned int i,m;
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);
LCD_CS_CLR;
LCD_RS_SET;
for(i=0;i<lcddev.height;i++)
{
for(m=0;m<lcddev.width;m++)
{
Lcd_WriteData_16Bit(Color);
}
}
LCD_CS_SET;
}
/*****************************************************************************
* @name :void LCD_RESET(void)
* @date :2018-08-09
* @function :Reset LCD screen
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_RESET(void)
{
LCD_RST_CLR;
HAL_Delay(100);
LCD_RST_SET;
HAL_Delay(50);
}
/*****************************************************************************
* @name :void LCD_Init(void)
* @date :2018-08-09
* @function :Initialization LCD screen
* @parameters :None
* @retvalue :None
******************************************************************************/
void LCD_Init(void)
{
//MX_GPIO_Init();
LCD_RESET(); //LCD 复位
//************* ST7735S初始化**********//
LCD_WR_REG(0x11);//Sleep exit
HAL_Delay(150);
//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(0xC0);
//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_direction(USE_HORIZONTAL);//设置LCD显示方向
LCD_BLK_SET;//点亮背光
LCD_Clear(WHITE);//清全屏白色
}
/*****************************************************************************
* @name :void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
* @date :2018-08-09
* @function :Setting LCD display window
* @parameters :xStar:the bebinning x coordinate of the LCD display window
yStar:the bebinning y coordinate of the LCD display window
xEnd:the endning x coordinate of the LCD display window
yEnd:the endning y coordinate of the LCD display window
* @retvalue :None
******************************************************************************/
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd)
{
LCD_WR_REG(lcddev.setxcmd);
LCD_WR_DATA(0x00);
LCD_WR_DATA(xStar);
LCD_WR_DATA(0x00);
LCD_WR_DATA(xEnd);
LCD_WR_REG(lcddev.setycmd);
LCD_WR_DATA(0x00);
LCD_WR_DATA(yStar);
LCD_WR_DATA(0x00);
LCD_WR_DATA(yEnd);
LCD_WriteRAM_Prepare(); //开始写入GRAM
}
/*****************************************************************************
* @name :void LCD_SetCursor(u16 Xpos, u16 Ypos)
* @date :2018-08-09
* @function :Set coordinate value
* @parameters :Xpos:the x coordinate of the pixel
Ypos:the y coordinate of the pixel
* @retvalue :None
******************************************************************************/
void LCD_SetCursor(u16 Xpos, u16 Ypos)
{
LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);
}
/*****************************************************************************
* @name :void LCD_direction(u8 direction)
* @date :2018-08-09
* @function :Setting the display direction of LCD screen
* @parameters :direction:0-0 degree
1-90 degree
2-180 degree
3-270 degree
* @retvalue :None
******************************************************************************/
void LCD_direction(u8 direction)
{
lcddev.setxcmd=0x2A;
lcddev.setycmd=0x2B;
lcddev.wramcmd=0x2C;
switch(direction){
case 0:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
LCD_WriteReg(0x36,(0<<3)|(1<<6)|(1<<7));//BGR==1,MY==0,MX==0,MV==0
break;
case 1:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
LCD_WriteReg(0x36,(0<<3)|(1<<7)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
break;
case 2:
lcddev.width=LCD_W;
lcddev.height=LCD_H;
LCD_WriteReg(0x36,(0<<3)|(0<<6)|(0<<7));//BGR==1,MY==0,MX==0,MV==0
break;
case 3:
lcddev.width=LCD_H;
lcddev.height=LCD_W;
LCD_WriteReg(0x36,(0<<3)|(0<<7)|(1<<6)|(1<<5));//BGR==1,MY==1,MX==0,MV==1
break;
default:break;
}
}
#ifndef _SPI_LCD_H_
#define _SPI_LCD_H_
#include "main.h"
#include "stdlib.h"
#include "gpio.h"
#define u32 unsigned int
#define u16 unsigned short
#define u8 unsigned char
//液晶控制口置1操作语句宏定义
#define SPI_MOSI_SET HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
#define SPI_SCLK_SET HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
//液晶控制口置0操作语句宏定义
#define SPI_MOSI_CLR HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
#define SPI_SCLK_CLR HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
void SPIv_WriteData(unsigned char Data);
//LCD重要参数集
typedef struct
{
u16 width; //LCD 宽度
u16 height; //LCD 高度
u16 id; //LCD ID
u8 dir; //横屏还是竖屏控制:0,竖屏;1,横屏。
u16 wramcmd; //开始写gram指令
u16 setxcmd; //设置x坐标指令
u16 setycmd; //设置y坐标指令
}_lcd_dev;
//LCD参数
extern _lcd_dev lcddev; //管理LCD重要参数
/用户配置区///
#define USE_HORIZONTAL 0//定义液晶屏顺时针旋转方向 0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转
//
//定义LCD的尺寸
#define LCD_W 128
#define LCD_H 160
//TFTLCD部分外要调用的函数
extern u16 POINT_COLOR;//默认红色
extern u16 BACK_COLOR; //背景颜色.默认为白色
//----------------- LCD端口定义直接不需要了,只需要在STM32CubeMX上设置好IO口并且标签打好,直接连接就能用 ----------------
#define LCD_BLK_SET HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
//LCD背光打开,QDtech全系列模块采用了三极管控制背光亮灭,用户也可以接PWM调节背光亮度
//如果使用官方库函数定义下列底层,速度将会下降到14帧每秒,建议采用他们公司推荐方法
//以下IO定义直接操作寄存器,快速IO操作,刷屏速率可以达到28帧每秒!
//GPIO功能口置位(拉高)
#define LCD_CS_SET HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
#define LCD_RS_SET HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); //数据/命令 (DC RS 指的是同一个引脚哈)
#define LCD_RST_SET HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); //复位
//GPIO功能口复位(拉低)
#define LCD_CS_CLR HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
#define LCD_RS_CLR HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); //数据/命令
#define LCD_RST_CLR HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); //复位
//画笔颜色
#define WHITE 0xFFFF
#define BLACK 0x0000
#define BLUE 0x001F
#define BRED 0XF81F
#define GRED 0XFFE0
#define GBLUE 0X07FF
#define RED 0xF800
#define MAGENTA 0xF81F
#define GREEN 0x07E0
#define CYAN 0x7FFF
#define YELLOW 0xFFE0
#define BROWN 0XBC40 //棕色
#define BRRED 0XFC07 //棕红色
#define GRAY 0X8430 //灰色
//GUI颜色
#define DARKBLUE 0X01CF //深蓝色
#define LIGHTBLUE 0X7D7C //浅蓝色
#define GRAYBLUE 0X5458 //灰蓝色
//以上三色为PANEL的颜色
#define LIGHTGREEN 0X841F //浅绿色
#define LIGHTGRAY 0XEF5B //浅灰色(PANNEL)
#define LGRAY 0XC618 //浅灰色(PANNEL),窗体背景色
#define LGRAYBLUE 0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE 0X2B12 //浅棕蓝色(选择条目的反色)
void SPIv_WriteData(u8 Data);
void LCD_WR_REG(u8 data);
void LCD_WR_DATA(u8 data);
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue);
void LCD_WriteRAM_Prepare(void);
void Lcd_WriteData_16Bit(u16 Data);
void LCD_DrawPoint(u16 x,u16 y);
void LCD_Clear(u16 Color);
void LCD_RESET(void);
void LCD_Init(void);
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd);
void LCD_SetCursor(u16 Xpos, u16 Ypos);
void LCD_direction(u8 direction);
#endif
第一个C文件没什么好说的,就是根据通讯时序写的,自己可以练习写一写通讯时序,作为小白的我就直接白嫖了哈哈哈哈。第二个H文件,里面对引脚的一些宏定义,要注意根据自己设定的修改,相应的拉高或者拉低电平,要记得背光引脚电平拉高,不信的小伙伴可以试一试。
LCD显示函数就没有什么好说的了,直接用就行。 接下来介绍两个好用的函数,画网格和lcd显示波形。 1.画网格
/*****************************************************************************
**自己编写的函数
名称:void LCD_Drawgrid(int wid,int lcd_wid,int lcd_high)
功能:画网格
参数:网格宽度,lcd屏幕宽度,LCD屏幕高度
******************************************************************************/
void LCD_Drawgrid(int wid,int lcd_wid,int lcd_high)
{
float row,col;
int nrow,ncol;
lcd_wid = 128;
lcd_high = 160;
row = (float)lcd_wid/wid;
col = (float)lcd_high/wid;
nrow = (int)lcd_wid/wid;
ncol = (int)lcd_high/wid;
if(row>nrow)
nrow = nrow + 1;
if(row==nrow)
nrow = nrow;
if(col>ncol)
ncol = ncol + 1;
if(col==ncol)
ncol = ncol;
for(int i = 0; i <ncol; i++)
{
for(int j = 0; j <nrow; j++)
{
LCD_DrawLine(0,i*wid,lcd_wid,i*wid);
LCD_DrawLine(j*wid,0,j*wid,lcd_high);
}
}
}
2.显示波形
/*****************************************************************************
名称:void drawCurve(short int rawValue)
功能:画波形
参数:波形数据
注意: uint16_t lastX,lastY; 移植前别忘记定义这两
unsigned char firstPoint = 1;
******************************************************************************/
uint16_t lastX,lastY;
unsigned char firstPoint = 1;
void drawCurve(short int rawValue)
{
uint16_t x,y;
y = 100-rawValue/100; //起始坐标y的值,改变函数幅值
//这里之所以是120-rawValue/280,与屏幕的扫描方向有关,如果出现上下颠倒的情况,可以改成120 +
if(firstPoint)//如果是第一次画点,则无需连线,直接描点即可
{
LCD_DrawPoint(0,y);
lastX=0;
lastY=y;
firstPoint=0;
}
else
{
x=lastX+1;
if(x<160) //不超过屏幕宽度
{
LCD_DrawLine(lastY,lastX,y,x);
HAL_Delay(20);
lastX=x;
lastY=y;
}
else //超出屏幕宽度,清屏,从第一个点开始绘制,实现动态更新效果
{
LCD_Clear(WHITE);//清屏,白色背景
LCD_Drawgrid(20,128,160);
HAL_Delay(300);
LCD_DrawPoint(0,y);
lastX=0;
lastY=y;
}
}
}
第二个函数是csdn上另外一位博主的,链接忘记了 它可以根据数据显示波形,最好把数据放在一个数组里面,然后用for循环取出来,
int a[100] = {2112,2241,2368,2495,2619,2742,2861,2978,3091,3199,3303,3402,3496,3584,3666,3742,3811,
3873,3928,3975,4015,4047,4071,4087,4095,4095,4087,4071,4047,4015,3975,3928,3873,
3811,3742,3666,3584,3496,3402,3303,3199,3091,2978,2861,2742,2619,2495,2368,2241,
2112,1984,1855,1728,1601,1477,1354,1235,1118,1005,897,793,694,600,512,430,
354,285,223,168,121,81,49,25,9,1,1,9,25,49,81,121,
168,223,285,354,430,512,600,694,793,897,1005,1118,1235,1354,1477,1601,
1728,1855,1984,};
可以用这组数据测试。 放在while(1)里面。
分享一下淘宝的例程资料下载:https://pan.baidu.com/s/1bp6AYsR 分享结束,欢迎指正
|