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

目录

概述

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 */
}

运行结果如下:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年最新趋势跨境电商平台开发需了解的新技术

随着数字化技术的不断演进和全球市场的日益融合&#xff0c;跨境电商平台开发将面临前所未有的挑战和机遇。为了更好地适应并引领这一发展&#xff0c;开发者需要密切关注2024年最新的技术趋势&#xff0c;以确保他们的平台能够在竞争激烈的市场中脱颖而出。本文将对跨境电商平…

音视频-H264编码封装- MP4格式转Annex B格式

目录 1&#xff1a;H264语法结构回顾 2&#xff1a;H264编码补充介绍 3&#xff1a;MP4模式转Annex B模式输出到文件示例 1&#xff1a;H264语法结构回顾 在之前文章里介绍过H264的语法结构。 传送门: 视音频-H264 编码NALU语法结构简介 2&#xff1a;H264编码补充介绍 H…

镓未来助力联想笔记本GaN适配器标配化,赋能高效用户体验

镓未来赋能笔记本电脑GaN适配器标配化 据悉&#xff0c;Lenovo 2024年推出搭配的多款新型笔记本原装适配器电源ADL100UDGC3A&#xff0c;采用了镓未来集成型Cascode技术氮化镓功率器件G1N65R150PB。新款方案相较上一代工艺&#xff0c;体积减小23%&#xff0c;重量降低18%&…

JavaScript之数据类型(3)——object进阶

前言&#xff1a; 利用基础知识来构建对象会发现十分复杂&#xff0c;我们可以结合其他的知识点来为我们object的构建进行优化。 <1>工厂法&#xff1a; 基本格式&#xff1a; function creatObject(属性值1,属性值2,属性值3,...,属性值n) {var 对象名 new Object();对…

parallels desktop19最新免费Mac电脑虚拟机软件

Parallels Desktop是一款运行在Mac电脑上的虚拟机软件&#xff0c;它允许用户在Mac系统上同时运行多个操作系统&#xff0c;比如Windows、Linux等。通过这款软件&#xff0c;Mac用户可以轻松地在同一台电脑上体验不同操作系统的功能和应用程序&#xff0c;而无需额外的硬件设备…

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…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-15.5讲 GPIO中断实验-通用中断驱动编写

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

MYSQL中的DQL

语法&#xff1a; select 字段列表 from 表名列表 where 条件列表 group by 分组字段列表 having 分组后条件列表 order by 排序字段 limit 分页参数 条件查询 语法&#xff1a; 查询多个字段&#xff1a;select 字段1&#xff0c;字段2 from表名 查询所有字段&#xff1a…

计算机视觉——OpenCV实现Lucas-Kanade 光流追踪

1.光流 光流法是计算机视觉中用于估计图像序列中物体运动的关键技术。它类似于观察夜空中的彗星&#xff0c;通过其在天空中的运动轨迹来追踪它的路径。在图像处理中&#xff0c;光流帮助我们理解像素点如何在连续的帧之间移动。 1.1 稀疏光流法 稀疏光流法关注于图像中的关…

【算法】最短路问题 bfs 到 dijkstra

1976、到达目的地的方案数 你在一个城市里&#xff0c;城市由 n 个路口组成&#xff0c;路口编号为 0 到 n - 1 &#xff0c;某些路口之间有 双向 道路。输入保证你可以从任意路口出发到达其他任意路口&#xff0c;且任意两个路口之间最多有一条路。 给你一个整数 n 和二维整…

Coze扣子开发指南:AI零代码编程创建插件

在Coze扣子中创建插件&#xff0c;有两种方式&#xff0c;一是用API&#xff0c;具体方式参照上一篇文章《Coze扣子开发指南&#xff1a;用免费API自己创建插件》&#xff0c;还有一种方式就是编程&#xff0c;不过有了AI的帮助&#xff0c;即使不会编程的人&#xff0c;也可以…

AI语音模型PaddleSpeech踩坑(安装)指南

PaddleSpeech简介 PaddleSpeech 是基于飞桨 PaddlePaddle 的语音方向的开源模型库&#xff0c;用于语音和音频中的各种关键任务的开发&#xff0c;包含大量基于深度学习前沿和有影响力的模型。 PaddleSpeech安装步骤 提示&#xff1a;要找到一个合适的PaddleSpeech版本与pad…

软件全套资料梳理(需求、开发、实施、运维、安全、测试、交付、认证、评审、投标等)

软件全套精华资料包清单部分文件列表&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&#xff0c;用户需求说明书&#xff0c;概要设计说明书&#xff0c…

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

题目&#xff1a; 题解&#xff1a; class Solution { public:int largestRectangleArea(vector<int>& heights) {int n heights.size();vector<int> left(n), right(n, n);stack<int> mono_stack;for (int i 0; i < n; i) {while (!mono_stack.em…

批量生成大量附件(如:excel,txt,pdf)压缩包等文件时前端超时,采用mq+redis异步处理和多线程优化提升性能

一.首先分析一下场景&#xff1a;项目中我需要从财务模块去取单证模块的数据来生成一个个excel文件 在单证那个一个提单号就是一个excel文件&#xff0c;我们这边一个财务发票可能会查出几千个提单&#xff0c;也就是会生成几百个excel&#xff0c;然后压缩为一个压缩包&#x…

企业微信创建应用(一)

登录到企业微信后台管理(https://work.weixin.qq.com/)进入自建应用(应用管理-应用-创建应用) 3.查看参数AgentId和 Secret 4.企业微信查看效果

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…

音频运放LPF使用

1、用于差分ADC输入的单端到差分缓冲器和抗混叠LPF 2、用于单端ADC输入的缓冲和抗混叠LPF 3、用于DAC输出的后置LPF和差分到单端缓冲器&#xff08;AC耦合&#xff09; 4、后LPF和差分到DAC输出的单端缓冲器&#xff08;直流耦合&#xff09; 5、用于差分ADC输入的具有抗混叠LP…

【无标获取S4与ECC的具体差异的方法题】

首先我们需要对ECC vs S4的差异这个课题要有一个深刻的理解&#xff0c;这不是一个简单并能准确说清楚的课题。 我们需要结合实际项目的具体情况去回答这个问题&#xff0c;因为这个问题本身是没有标准答案的。 首先要了解SAP本身ERP产品线的发展概况&#xff0c;其次我们要…

微信小程序踩坑,skyline模式下,scroll-view下面的一级元素设置margin中的auto无效,具体数据有效

开发工具版本 基础库 开启skyline渲染调试 问题描述 skyline模式下,scroll-view下面的一级元素的margin写auto的值是没有效果的(二级元素margin写auto是有效果的),关闭这个模式就正常显示 演示效果图 父元素的宽度和高度效果(宽度是750rpx,宽度占满的) 一级元素宽度和css效果…