使用SPI驱动串行LCD的驱动实现(STM32F4)

news2024/12/25 23:37:23

目录

概述

1. 硬件介绍

1.1 ST7796-LCD

1.2 MCU IO与LCD PIN对应关系

2 功能实现

2.1 使用STM32Cube配置Project

 2.2 STM32Cube生成工程

3 代码实现

3.1 SPI接口实现

3.2 LCD驱动程序实现

3.3 测试程序实现

 4 测试


源代码下载地址:

https://gitee.com/mftang/stm32_open_test_proj/tree/master/stm32_f407_lcd_proj/UserCode/lcd_drv

测试视频:

使用SPI驱动串行LCD的驱动实现(STM32F4)

概述

本文主要讲述使用STM32硬件SPI接口驱动ST7796-LCD,主控MCU为STM32F407芯片。笔者详细介绍整个驱动的实现过程,并使用STM32Cube生成一个工程,测试驱动程序的功能。

1. 硬件介绍

1.1 ST7796-LCD

LCD的PIN引脚功能介绍

序号模块引脚引脚说明
1VCC屏电源正
2GND屏电源地
3LCD_CS液晶屏片选控制信号,低电平有效
4LCD_RST液晶屏复位控制信号,低电平复位
5LCD_RS液晶屏命令/数据选择控制信号

高电平:数据,低电平:命令

6SDI(MOSI)SPI总线写数据信号(SD卡和液晶屏共用)
7SCKSPI总线时钟信号(SD卡和液晶屏共用)
8LED液晶屏背光控制信号(如需要控制,请接引脚,如不需要控制,可以不接)
9SDO(MISO)SPI总线读数据信号(SD卡和液晶屏共用)
10CTP_SCL电容触摸屏IIC总线时钟信号(无触摸屏的模块不需连接)
11CTP_RST电容触摸屏复位控制信号,低电平复位(无触摸屏的模块不需连接)
12CTP_SDA电容触摸屏IIC总线数据信号(无触摸屏的模块不需连接)
13CTP_INT电容触摸屏IIC总线触摸中断信号,产生触摸时,输入低电平到主控(无触摸屏的模块不需连接)
14SD_CSSD卡片选控制信号,低电平有效(不使用SD卡功能,可不接)

实体LCD Port对应关系如下图所示

1.2 MCU IO与LCD PIN对应关系

STM32 PIN引脚LCD PIN引脚
PB5-MOSIMOSI
PB4-MISOMISO
PB3-SCKSCK
PB6CS
PB9RST
PB8RS

2 功能实现

2.1 使用STM32Cube配置Project

1) 配置SPI接口

SPI的参数

2)配置LCD的控制引脚

3)使能外部晶振

 2.2 STM32Cube生成工程

使用STM32Cube生成工程,并创建两个目录

User/lcd_drv     驱动文件目录

User/test           测试文件目录

3 代码实现

3.1 SPI接口实现

在spi.c文件中实现读写接口函数,具体实现如下:

/* USER CODE BEGIN 1 */

void hal_spi_writebyte( uint8_t byte )
{
    uint8_t buff[1];
    
    buff[0] = byte;
    HAL_SPI_Transmit( &hspi3, buff, 1, 1000);
}

uint8_t hal_spi_readbyte(void)
{
    uint8_t buff[1];
    
    buff[0] = 0xff;
    return HAL_SPI_Receive( &hspi3, buff, 1, 1000);
}

/* USER CODE END 1 */

3.2 LCD驱动程序实现

创建lcd_drv.c实现驱动程序,lcd_spi.c实现和MCU之间的驱动接口

 1)lcd_drv.c 程序实现

#include "lcd_drv.h"
#include "lcd_spi.h"

_lcd_dev lcddev;

void LCD_WR_REG(uint8_t data)
{ 
    LCD_CS_CLR;
    LCD_RS_CLR; 
    
    SPI_WriteByte(data);
    LCD_CS_SET;
}

void LCD_WR_DATA(uint8_t data)
{
    LCD_CS_CLR;
    LCD_RS_SET;
    SPI_WriteByte(data);
    LCD_CS_SET;
}

uint8_t LCD_RD_DATA(void)
{
    uint8_t data;
    
    LCD_CS_CLR;
    LCD_RS_SET;
    data = SPI_ReadByte();
    LCD_CS_SET;
    
    return data;
}

void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
{
    LCD_WR_REG(LCD_Reg);  
    LCD_WR_DATA(LCD_RegValue); 
}

uint8_t LCD_ReadReg(uint8_t LCD_Reg)
{
    LCD_WR_REG(LCD_Reg);
    return LCD_RD_DATA();
}

void LCD_WriteRAM_Prepare(void)
{
    LCD_WR_REG(lcddev.wramcmd);
}

void Lcd_WriteData_16Bit(uint16_t Data)
{
    LCD_CS_CLR;
    LCD_RS_SET;
    SPI_WriteByte(Data>>8);
    SPI_WriteByte(Data);
    LCD_CS_SET;
}

uint16_t Lcd_ReadData_16Bit(void)
{
    uint16_t r,g;
    
    LCD_CS_CLR;
    LCD_RS_CLR;
    
    SPI_WriteByte(lcddev.rramcmd);
    LCD_RS_SET;
    SPI_ReadByte();
    r = SPI_ReadByte();
    g = SPI_ReadByte();
    LCD_CS_SET;
    r<<=8;
    r|=g;
    
    return r;
}

void LCD_DrawPoint(uint16_t x,uint16_t y, uint16_t color)
{
    LCD_SetCursor(x,y);
    Lcd_WriteData_16Bit(color); 
}

uint16_t LCD_ReadPoint(uint16_t x,uint16_t y)
{
    uint16_t color;
    
    LCD_SetCursor(x,y);
    color = Lcd_ReadData_16Bit();
    
    return color;
}

void LCD_Clear(uint16_t Color)
{
    uint16_t 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++)
        {
            SPI_WriteByte(Color>>8);
            SPI_WriteByte(Color);
        }
    }
    LCD_CS_SET;
} 

void LCD_SetWindows(uint16_t xStar, uint16_t yStar,uint16_t xEnd,uint16_t yEnd)
{
    LCD_WR_REG(lcddev.setxcmd);
    LCD_WR_DATA(xStar>>8);
    LCD_WR_DATA(0x00FF&xStar);
    LCD_WR_DATA(xEnd>>8);
    LCD_WR_DATA(0x00FF&xEnd);

    LCD_WR_REG(lcddev.setycmd);
    LCD_WR_DATA(yStar>>8);
    LCD_WR_DATA(0x00FF&yStar);
    LCD_WR_DATA(yEnd>>8);
    LCD_WR_DATA(0x00FF&yEnd);

    LCD_WriteRAM_Prepare();
} 

void LCD_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
    LCD_SetWindows(Xpos,Ypos,Xpos,Ypos);
}

void LCD_direction(uint8_t direction)
{ 
    lcddev.setxcmd=0x2A;
    lcddev.setycmd=0x2B;
    lcddev.wramcmd=0x2C;
    lcddev.rramcmd=0x2E;
    lcddev.dir = direction%4;
    
    switch(lcddev.dir){  
        case 0:
            lcddev.width=LCD_W;
            lcddev.height=LCD_H;
            LCD_WriteReg(0x36,(1<<3)|(1<<6));
        break;
        
        case 1:
            lcddev.width=LCD_H;
            lcddev.height=LCD_W;
            LCD_WriteReg(0x36,(1<<3)|(1<<5));
        break;
        
        case 2:
            lcddev.width=LCD_W;
            lcddev.height=LCD_H;
            LCD_WriteReg(0x36,(1<<3)|(1<<7));
        break;
        
        case 3:
            lcddev.width=LCD_H;
            lcddev.height=LCD_W;
            LCD_WriteReg(0x36,(1<<3)|(1<<7)|(1<<6)|(1<<5));
        break;
        default:break;
    }
} 

uint16_t LCD_Read_ID(void)
{
    uint8_t i,val[3] = {0};

    LCD_WR_REG(0xF0);     // Command Set Control
    LCD_WR_DATA(0xC3);   

    LCD_WR_REG(0xF0);     
    LCD_WR_DATA(0x96);  
    LCD_CS_CLR;

    for(i=1;i<4;i++)
    {
        LCD_RS_CLR;	  
        SPI_WriteByte(0xFB);
        LCD_RS_SET;
        SPI_WriteByte(0x10+i);
        LCD_RS_CLR;	  
        SPI_WriteByte(0xD3);
        LCD_RS_SET;
        val[i-1] = SPI_ReadByte();
        LCD_RS_CLR;	  
        SPI_WriteByte(0xFB);
        LCD_RS_SET;
        SPI_WriteByte(0x00);
    }
    
    LCD_CS_SET;
    LCD_WR_REG(0xF0);     // Command Set Control
    LCD_WR_DATA(0x3C);   
    LCD_WR_REG(0xF0);     
    LCD_WR_DATA(0x69);  
    lcddev.id=val[1];
    lcddev.id<<=8;
    lcddev.id|=val[2];

    return lcddev.id;
}

void LCD_RESET(void)
{
    LCD_RST_CLR;
    lcd_delay_us(100);
    LCD_RST_SET;
    lcd_delay_us(50);
}

void LCD_Init(void)
{  
    LCD_RESET();          //LCD
    //*************3.5 ST7796S IPS
    LCD_WR_REG(0x11);     

    lcd_delay_us(120);    //Delay 120ms
    lcd_delay_us(120);    //Delay 120ms

    LCD_WR_REG(0x36);     // Memory Data Access Control MY,MX~~
    LCD_WR_DATA(0x48);   

    LCD_WR_REG(0x3A);     
    LCD_WR_DATA(0x55);   

    LCD_WR_REG(0xF0);     // Command Set Control
    LCD_WR_DATA(0xC3);   

    LCD_WR_REG(0xF0);     
    LCD_WR_DATA(0x96);   

    LCD_WR_REG(0xB4);     
    LCD_WR_DATA(0x01);   

    LCD_WR_REG(0xB7);     
    LCD_WR_DATA(0xC6);   

    //LCD_WR_REG(0xB9);     
    //LCD_WR_DATA(0x02);
    //LCD_WR_DATA(0xE0);

    LCD_WR_REG(0xC0);     
    LCD_WR_DATA(0x80);   
    LCD_WR_DATA(0x45);   

    LCD_WR_REG(0xC1);     
    LCD_WR_DATA(0x13);   //18  //00

    LCD_WR_REG(0xC2);     
    LCD_WR_DATA(0xA7);   

    LCD_WR_REG(0xC5);     
    LCD_WR_DATA(0x0A);   

    LCD_WR_REG(0xE8);     
    LCD_WR_DATA(0x40);
    LCD_WR_DATA(0x8A);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x29);
    LCD_WR_DATA(0x19);
    LCD_WR_DATA(0xA5);
    LCD_WR_DATA(0x33);

    LCD_WR_REG(0xE0);
    LCD_WR_DATA(0xD0);
    LCD_WR_DATA(0x08);
    LCD_WR_DATA(0x0F);
    LCD_WR_DATA(0x06);
    LCD_WR_DATA(0x06);
    LCD_WR_DATA(0x33);
    LCD_WR_DATA(0x30);
    LCD_WR_DATA(0x33);
    LCD_WR_DATA(0x47);
    LCD_WR_DATA(0x17);
    LCD_WR_DATA(0x13);
    LCD_WR_DATA(0x13);
    LCD_WR_DATA(0x2B);
    LCD_WR_DATA(0x31);

    LCD_WR_REG(0xE1);
    LCD_WR_DATA(0xD0);
    LCD_WR_DATA(0x0A);
    LCD_WR_DATA(0x11);
    LCD_WR_DATA(0x0B);
    LCD_WR_DATA(0x09);
    LCD_WR_DATA(0x07);
    LCD_WR_DATA(0x2F);
    LCD_WR_DATA(0x33);
    LCD_WR_DATA(0x47);
    LCD_WR_DATA(0x38);
    LCD_WR_DATA(0x15);
    LCD_WR_DATA(0x16);
    LCD_WR_DATA(0x2C);
    LCD_WR_DATA(0x32);


    LCD_WR_REG(0xF0);     
    LCD_WR_DATA(0x3C);   

    LCD_WR_REG(0xF0);     
    LCD_WR_DATA(0x69);   

    lcd_delay_us(120);

    LCD_WR_REG(0x21);     

    LCD_WR_REG(0x29); 

    LCD_direction(USE_HORIZONTAL);
    LCD_Clear(DARKBLUE);
}


/* End of this file */

2)lcd_spi.c 程序实现

#include "lcd_spi.h"

#if !IO_SPI
#include "spi.h"
#endif


void lcd_gpio_init(void)
{
#if IO_SPI
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();


  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LCD_GPIO_PORT, lcd_sck_Pin|lcd_mosi_Pin|lcd_cs_Pin|lcd_rs_Pin
                                   |lcd_rst_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : PBPin PBPin PBPin PBPin
                           PBPin */
  GPIO_InitStruct.Pin = lcd_sck_Pin|lcd_mosi_Pin|lcd_cs_Pin|lcd_rs_Pin
                          |lcd_rst_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LCD_GPIO_PORT, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = lcd_miso_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(lcd_miso_GPIO_Port, &GPIO_InitStruct);
#endif

}

void lcd_delay_us(uint32_t us)
{
    uint32_t i=0;
    
    while(us--){
        for(i=0;i<1000;i++);
    }
}

void SPI_WriteByte(uint8_t Byte)
{
#if IO_SPI
    uint8_t i=0;
    
    for(i=0;i<8;i++)
    {
        if(Byte&0x80)
        {
            SPI_MOSI_SET;
        }
        else
        {
            SPI_MOSI_CLR;
        }
        SPI_SCLK_CLR;
        SPI_SCLK_SET;
        Byte<<=1;
    }
#else
    hal_spi_writebyte( Byte );
#endif
} 

uint8_t SPI_ReadByte(void)
{
#if IO_SPI
    uint8_t value=0,i=0,byte=0xFF;

    for(i=0;i<8;i++)
    {
        value<<=1;
        if(byte&0x80)
        {
            SPI_MOSI_SET;
        }
        else
        {
            SPI_MOSI_CLR;
        }
        byte<<=1;
        SPI_SCLK_CLR;
        lcd_delay_us(100);
        if(SPI_MISO_READ)
        {
            value += 1;
        }
        
        SPI_SCLK_SET;
        lcd_delay_us(100);
    }
    return value;
#else
    return hal_spi_readbyte();
#endif
} 

3.3 测试程序实现

创建lcd_test.c文件,编写测试程序

 4 测试

在如下文件中调用测试程序,其主要实现每隔1s时间刷新屏幕的颜色。

详细代码:

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN StartDefaultTask */
   int count;
   LCD_Init();
   LCD_Read_ID();
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
    if(count%1000 == 0)
    {
        lcd_test();
    }
    count++;
  }
  /* USER CODE END StartDefaultTask */
}

运行结果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1665820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[蓝桥杯]真题讲解:AB路线(BFS+分层图)

[蓝桥杯]真题讲解&#xff1a;AB路线&#xff08;BFS分层图&#xff09; 一、视频讲解二、正解代码1、C2、python33、Java 一、视频讲解 [蓝桥杯]真题讲解&#xff1a;AB路线&#xff08;BFS分层图&#xff09; 二、正解代码 1、C #include<bits/stdc.h> #define INF …

大数据Scala教程从入门到精通第八篇:Scala在IDEA中编写Hello World

一&#xff1a;Scala在IDEA中编写Hello World object HelloWorld {def main(args: Array[String]): Unit {println("hello world")}}这个对象也单例的。 特殊的Java类库需要import

【Java 查询树结构列表,递归删除子节点】

Java 获取列表树结构&#xff0c;递归删除子节点 数据库表结构ModelVO查询树结构列表递归删除子节点 数据库表结构 Model Data AllArgsConstructor NoArgsConstructor public class TBaseDept {/** ID */private String id;/** 单位名称 */private String fdName;/** 部门编码…

Python | Leetcode Python题解之第84题柱状图中最大的矩形

题目&#xff1a; 题解&#xff1a; class Solution:def largestRectangleArea(self, heights: List[int]) -> int:n len(heights)left, right [0] * n, [n] * nmono_stack list()for i in range(n):while mono_stack and heights[mono_stack[-1]] > heights[i]:righ…

scanf留下的那一片云彩

【题目描述】 给出一个由O和X组成的串&#xff08;长度为1&#xff5e;80&#xff09;&#xff0c;统计得分。每个O的得分为目前连续出现的O的个数&#xff0c;X的得分为0。例如&#xff0c;OOXXOXXOOO的得分为1200100123。 输入第一行表示有n个字符串&#xff0c;后续是n行字…

1056: 邻接表到邻接矩阵

解法&#xff1a; #include<iostream> #include<vector> #include<string> using namespace std; int arr[100][100]; int main() {int n;cin >> n;getchar();vector<string> s(n);for (int i 0; i < n; i) {getline(cin, s[i]);}for (int …

进程间通信:连接不同程序世界的桥梁

目录 一、进程间通信的重要性 二、常见的进程间通信方式 三、进程间通信的目的 四、进程间通信的本质 在计算机编程的领域中&#xff0c;进程间通信&#xff08;Inter-Process Communication&#xff0c;IPC&#xff09;是一个至关重要的概念。当我们在操作系统中运行多个程…

数据结构之——队列详解

目录 前言&#xff1a; 一、什么是队列 二、队列的实现 2.1 队列结构 2.2 队列初始化 2.3 队列销毁 2.4 入队列 2.5 出队列 2.6 获取队列头部元素 2.7 获取队列尾部元素 2.8 获取队列中有效元素个数 2.9 检测队列是否为空 三、 代码总览 Queue.h test.c 四、例题 前言…

Base64在线编码解码方法

Base64在线编码解码 打开网站 在线工具网-梦幻加菲猫 选择“Base64编码解码” 输入需要编码/解码的内容&#xff0c;点击“编码”/“解码”按钮 编码&#xff1a; 解码&#xff1a; 4. 复制已经编码/解码后的内容。

大数据交通行政执法监测系统

交通行政执法监测系统应用系统按照监测主体可分为&#xff1a;出租车交通违法监测&#xff0c;客车交通违法监测&#xff0c;货车、危化品车辆交通违法监测&#xff0c;非法营运车辆监测。功能模块涵盖&#xff1a;特征识别、档案查询、预警分析等。 &#xff08;1&#xff09;…

腾讯云服务器之ssh远程连接登录及转发映射端口实现内网穿透(实现服务器访问本地电脑端口)

目录 一、创建密钥绑定实例二、设置私钥权限三、ssh远程连接到服务器四、修改root密码五、端口转发&#xff08;实现服务器访问本地电脑的端口&#xff09; 一、创建密钥绑定实例 创建密钥会自动下载一个私钥&#xff0c;把这个私钥复制到c盘 二、设置私钥权限 1、删除所有用户…

前端笔记-day04

文章目录 01-后代选择器02-子代选择器03-并集选择器04-交集选择器05-伪类选择器06-拓展-超链接伪类07-CSS特性-继承性08-CSS特性-层叠性09-CSS特性-优先级11-Emmet写法12-背景图13-背景图平铺方式14-背景图位置15-背景图缩放16-背景图固定17-background属性18-显示模式19-显示模…

NPOI生成word浮动图标

1、NPOI版本2.7.0, net框架4.8 2、安装OpenXMLSDKToolV25.msi 3、先创建一个word文档&#xff0c;并设置图片为浮于文字之上 4、OpenXML显示的结果 5、实际代码如下&#xff1a; public class GenerateWordDemo {public GenerateWordDemo(){}//https://blog.fileformat.co…

word-排版文本基本格式

1、文本的基本格式&#xff1a;字体格式、段落格式 2、段落&#xff1a;word排版的基本控制单位 3、每敲一次回车&#xff0c;为一个段落标记&#xff0c;注意区分换行符和段落标记&#xff0c;换行符为指向下的箭头&#xff0c;段落标记为带拐弯的箭头&#xff0c;换行符&…

C语言基础——循环语句

&#x1f33a;​&#x1f64f;&#x1f64f;&#x1f64f;欢迎大家观看&#xff0c;写的好的话希望三连感谢&#x1f64f;&#x1f64f;&#x1f64f;&#x1f33a; 文章目录 一、循环语句的介绍 二、不同循环语句的使用 1.while循环 1.1 while循环的使用方式 1.2 while循环的执…

Java | Leetcode Java题解之第84题柱状图中最大的矩形

题目&#xff1a; 题解&#xff1a; class Solution {public int largestRectangleArea(int[] heights) {int n heights.length;int[] left new int[n];int[] right new int[n];Arrays.fill(right, n);Deque<Integer> mono_stack new ArrayDeque<Integer>();f…

https免费证书获取

获取免费证书的网址&#xff1a; Certbot 1. 进入你的linux系统&#xff0c;先安装snapd&#xff0c; yum install snapd 2. 启动snapd service snapd start 3.安装 Certbot snap install --classic certbot 注意如下出现此错误时&#xff0c;需要先建立snap 软连接后&am…

MVCC 详解

介绍 MVCC&#xff0c;全称 Multi-Version Concurrency Control&#xff0c;即多版本并发控制 MVCC的目的主要是为了提高数据库并发性能&#xff0c;用更好的方式去处理读-写冲突&#xff0c;做到即使有读写冲突时&#xff0c;也能做到不加锁。 这里的多版本指的是数据库中同时…

Ubuntu 和 Windows之间无法复制粘贴问题解决方法

需要安装open-vm-tools&#xff0c;官方安装open-vm-tools的网址&#xff1a;安装 Open VM Tools (vmware.com)

vue 点击平滑到指定位置并绑定页面滑动效果

1.html元素 写出对应的数据块&#xff08;注意添加ref) 用于获取元素位置 <template><div class"index-page" ><div class"top-head" ref"index"><img src"logo.png" style"height: 40px;margin-right: 2…