LCD—STM32液晶显示(4.液晶控制代码讲解)

news2024/10/7 16:20:09

目录

STM32液晶控制代码讲解

液晶接口封装介绍

使用LCD的配置步骤

内存操作要使用volatile进行修饰

图形绘制实现

绘制矩形

重点补充


STM32液晶控制代码讲解

液晶接口封装介绍

指南者液晶接口原理图

左边DB00—DB15表示液晶屏的数据线引脚,分别对应STM32的FSMC外设的FSMC_D0—D15及对应的GPIO

霸道原理图如下

可以发现左边得到数据线都是一样的,右边的LCD_RST不同,我们GPIO只需要设置为普通的推挽输出即可。两个板子通过对比之后就可以连接,自己设计板子的时候,哪些引脚是固定不可改变的,哪些引脚是可以自己随便选择的(其中所有具有FSMC外设复用功能的GPIO都是固定的,不能改变)。

然后看右半边的控制引脚:

LCD_BL是背光引脚,在丝印里面表示是BK,该引脚也可以任意选择,设置为普通的推挽输出就可以了。

LCD_CS为片选引脚,对应着指南者的NE1、霸道的NE4,因此要注意霸道和指南者访问液晶屏的地址不同。

RD引脚和WE引脚分别为读使能和写使能

RS引脚对应着LCD的D/CX控制线,用于控制写入的是数据还是命令,根据之前的“使用FSMC模拟8080时序”章节的讲解,D/CX是需要连接到FSMC的地址线的,通过一根地址线来控制,对于指南者是连接到了A16

下面的5根LCD_TP引脚是用于触摸屏的,它们是直接连接到xpt2046(触摸控制芯片),暂时不涉及到,之后再讲解。

使用时,我们先配置好相应的结构体和GPIO输出方式,然后就可以直接使用指针进行读写操作,对应的通讯引脚会自动产生读写时序。

通过输入0Ch命令,来判断FSMC与LCD是否正常通信,正常通信会返回两个参数,第一个为无效参数,第二个参数为LCD的像素格式(16bit或18bit)

使用LCD的配置步骤

  • 初始化ILI9341的IO引脚
  • LCD FSMC 模式配置,初始化两个结构体
  • 点亮LCD背光灯
  • 复位ILI9341
  • 初始化ILI9341寄存器
  • 设置默认扫描方向

内存操作要使用volatile进行修饰

使用指针从内存中读取数据时,内存地址必须加__IO(在STM32中为volatile)修饰,防止编译器进行变量优化。

比如我们进行写入命令的操作(FSMC_Addr_ILI9341_CMD 为宏)

*( __IO uint16_t * ) ( FSMC_Addr_ILI9341_CMD ) = 0x0C;

如果不加__IO,由于进行读取内存操作的时候,CPU的编译器会将数据从内存(RAM)中先读取到寄存器中,再传给变量,进行重复读取的时候,编译器认为数据没有发生变化,没必要从内存中重新读取,会直接使用寄存器中缓存的值赋值给变量。

而对于写入操作,如果不加__IO,编译器第一次调用会向内存中存入数据0x0C,存入数据成功后,同时LCD会产生相应的写入命令时序;而编译器第二次调用写入命令操作,会认为内存地址FSMC_Addr_ILI9341_CMD中的数据根本没有改变(因为上一次已经写入了0x0C),所以编译器会进行优化,认为没必要再次进行赋值操作,也就不再向内存地址中存入数据0x0C,因此LCD也就不会产生相应的写入命令时序。因此也就会导致LCD写入命令无效。同样读取数据和写入数据的操作同样要加__IO。

注:为了严谨起见,所有的内存操作都应该加上volatile。

图形绘制实现

两种方法:

方法一:首先要再显示器上开辟一个窗口,然后调用填充像素命令(0x2C),接着向窗口中写入像素值,从而实现图形的绘制。

 开辟窗口函数

 

/**
 * @brief  在ILI9341显示器上开辟一个窗口
 * @param  usX :在特定扫描方向下窗口的起点X坐标
 * @param  usY :在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth :窗口的宽度
 * @param  usHeight :窗口的高度
 * @retval 无
 */
void ILI9341_OpenWindow ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight )
{    
    ILI9341_Write_Cmd ( CMD_SetCoordinateX );                  /* 设置X坐标 */
    ILI9341_Write_Data ( usX >> 8  );     /* 先高8位,然后低8位 */
    ILI9341_Write_Data ( usX & 0xff  );     /* 设置起始点和结束点*/
    ILI9341_Write_Data ( ( usX + usWidth - 1 ) >> 8  );
    ILI9341_Write_Data ( ( usX + usWidth - 1 ) & 0xff  );

    ILI9341_Write_Cmd ( CMD_SetCoordinateY );                  /* 设置Y坐标*/
    ILI9341_Write_Data ( usY >> 8  );
    ILI9341_Write_Data ( usY & 0xff  );
    ILI9341_Write_Data ( ( usY + usHeight - 1 ) >> 8 );
    ILI9341_Write_Data ( ( usY + usHeight - 1) & 0xff );
    
}

方法二:使用填充单个像素点的操作,对相应位置进行像素填充,从而实现图形绘制

/**
 * @brief  对ILI9341显示器的某一点以某种颜色进行填充
 * @param  usX :在特定扫描方向下该点的X坐标
 * @param  usY :在特定扫描方向下该点的Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_SetPointPixel ( uint16_t usX, uint16_t usY )    
{    
    if ( ( usX < LCD_X_LENGTH ) && ( usY < LCD_Y_LENGTH ) )
  {
        ILI9341_SetCursor ( usX, usY );
        
        ILI9341_FillColor ( 1, CurrentTextColor );
    }
    
}

绘制矩形

方法一绘制实心矩形:

其中RGB888_2_RGB565(R,G,B) 函数可以显示自定义彩色

#define RGB888_2_RGB565(R,G,B) (uint16_t)(((R&1F)

方法二绘制空心矩形:

/**
 * @brief  在 ILI9341 显示器上画一个矩形
 * @param  usX_Start :在特定扫描方向下矩形的起始点X坐标
 * @param  usY_Start :在特定扫描方向下矩形的起始点Y坐标
 * @param  usWidth:矩形的宽度(单位:像素)
 * @param  usHeight:矩形的高度(单位:像素)
 * @param  ucFilled :选择是否填充该矩形
  *   该参数为以下值之一:
  *     @arg 0 :空心矩形
  *     @arg 1 :实心矩形 
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DrawRectangle ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight, uint8_t ucFilled )
{
    if ( ucFilled )
    {
        ILI9341_OpenWindow ( usX_Start, usY_Start, usWidth, usHeight );
        ILI9341_FillColor ( usWidth * usHeight ,CurrentTextColor);    
    }
    else
    {
        ILI9341_DrawLine ( usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start );
        ILI9341_DrawLine ( usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );
        ILI9341_DrawLine ( usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1 );
        ILI9341_DrawLine ( usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1 );        
    }

}

绘制直线

方法一:

方法二:

/**
 * @brief  在 ILI9341 显示器上使用 Bresenham 算法画线段 
 * @param  usX1 :在特定扫描方向下线段的一个端点X坐标
 * @param  usY1 :在特定扫描方向下线段的一个端点Y坐标
 * @param  usX2 :在特定扫描方向下线段的另一个端点X坐标
 * @param  usY2 :在特定扫描方向下线段的另一个端点Y坐标
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DrawLine ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 )
{
    uint16_t us; 
    uint16_t usX_Current, usY_Current;
    
    int32_t lError_X = 0, lError_Y = 0, lDelta_X, lDelta_Y, lDistance; 
    int32_t lIncrease_X, lIncrease_Y;     
    
    
    lDelta_X = usX2 - usX1; //计算坐标增量 
    lDelta_Y = usY2 - usY1; 
    
    usX_Current = usX1; 
    usY_Current = usY1; 
    
    
    if ( lDelta_X > 0 ) 
        lIncrease_X = 1; //设置单步方向 
    
    else if ( lDelta_X == 0 ) 
        lIncrease_X = 0;//垂直线 
    
    else 
  { 
    lIncrease_X = -1;
    lDelta_X = - lDelta_X;
  } 

    
    if ( lDelta_Y > 0 )
        lIncrease_Y = 1; 
    
    else if ( lDelta_Y == 0 )
        lIncrease_Y = 0;//水平线 
    
    else 
  {
    lIncrease_Y = -1;
    lDelta_Y = - lDelta_Y;
  } 

    
    if (  lDelta_X > lDelta_Y )
        lDistance = lDelta_X; //选取基本增量坐标轴 
    
    else 
        lDistance = lDelta_Y; 

    
    for ( us = 0; us <= lDistance + 1; us ++ )//画线输出 
    {  
        ILI9341_SetPointPixel ( usX_Current, usY_Current );//画点 
        
        lError_X += lDelta_X ; 
        lError_Y += lDelta_Y ; 
        
        if ( lError_X > lDistance ) 
        { 
            lError_X -= lDistance; 
            usX_Current += lIncrease_X; 
        }  
        
        if ( lError_Y > lDistance ) 
        { 
            lError_Y -= lDistance; 
            usY_Current += lIncrease_Y; 
        } 
        
    }  
    
    
}   

设置显示方向

/**
 * @brief  设置ILI9341的GRAM的扫描方向 
 * @param  ucOption :选择GRAM的扫描方向 
 *     @arg 0-7 :参数可选值为0-7这八个方向
 *
 *    !!!其中0、3、5、6 模式适合从左至右显示文字,
 *                不推荐使用其它模式显示文字    其它模式显示文字会有镜像效果            
 *        
 *    其中0、2、4、6 模式的X方向像素为240,Y方向像素为320
 *    其中1、3、5、7 模式下X方向像素为320,Y方向像素为240
 *
 *    其中 6 模式为大部分液晶例程的默认显示方向
 *    其中 3 模式为摄像头例程使用的方向
 *    其中 0 模式为BMP图片显示例程使用的方向
 *
 * @retval 无
 * @note  坐标图例:A表示向上,V表示向下,<表示向左,>表示向右
                    X表示X轴,Y表示Y轴
*/
void ILI9341_GramScan ( uint8_t ucOption )
{    
    //参数检查,只可输入0-7
    if(ucOption >7 )
        return;
    
    //根据模式更新LCD_SCAN_MODE的值,主要用于触摸屏选择计算参数
    LCD_SCAN_MODE = ucOption;
    
    //根据模式更新XY方向的像素宽度
    if(ucOption%2 == 0)    
    {
        //0 2 4 6模式下X方向像素宽度为240,Y方向为320
        LCD_X_LENGTH = ILI9341_LESS_PIXEL;
        LCD_Y_LENGTH =    ILI9341_MORE_PIXEL;
    }
    else                
    {
        //1 3 5 7模式下X方向像素宽度为320,Y方向为240
        LCD_X_LENGTH = ILI9341_MORE_PIXEL;
        LCD_Y_LENGTH =    ILI9341_LESS_PIXEL; 
    }

    //0x36命令参数的高3位可用于设置GRAM扫描方向    
    ILI9341_Write_Cmd ( 0x36 );
  if(lcdid == LCDID_ILI9341)
  {
    ILI9341_Write_Data ( 0x08 |(ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
  }
  else if(lcdid == LCDID_ST7789V)
  {
    ILI9341_Write_Data ( 0x00 |(ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
  }
    ILI9341_Write_Cmd ( CMD_SetCoordinateX ); 
    ILI9341_Write_Data ( 0x00 );        /* x 起始坐标高8位 */
    ILI9341_Write_Data ( 0x00 );        /* x 起始坐标低8位 */
    ILI9341_Write_Data ( ((LCD_X_LENGTH-1)>>8)&0xFF ); /* x 结束坐标高8位 */    
    ILI9341_Write_Data ( (LCD_X_LENGTH-1)&0xFF );                /* x 结束坐标低8位 */

    ILI9341_Write_Cmd ( CMD_SetCoordinateY ); 
    ILI9341_Write_Data ( 0x00 );        /* y 起始坐标高8位 */
    ILI9341_Write_Data ( 0x00 );        /* y 起始坐标低8位 */
    ILI9341_Write_Data ( ((LCD_Y_LENGTH-1)>>8)&0xFF );    /* y 结束坐标高8位 */     
    ILI9341_Write_Data ( (LCD_Y_LENGTH-1)&0xFF );                /* y 结束坐标低8位 */

    /* write gram start */
    ILI9341_Write_Cmd ( CMD_SetPixel );    
}

重点补充

工程中使用0x6D00 0000地址向液晶屏发送数据,使用0x6C00 0000地址向液晶屏发送命令,但实际上使得地址线FSMC_A23输出高低电平的并不是只有这两个地址,因为只是用到了对应那个地址位的0,1与其他位无关,所以只要是那个bank的地址都可以。因此可以随便改变其它位的地址,只要这个不影响这个位和在地址范围内就行

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

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

相关文章

使用ppocr突然退出问题

本次使用conda装了一个cuda10.2版本的paddleocr&#xff0c;然后所有的环境检查没问题&#xff0c;使用paddle自带的检查代码&#xff0c;输出提醒paddle可以正常使用&#xff1a; >>> import paddle >>> paddle.utils.run_check() 输出结果提示安装正常 …

零编程经验也能打造精美微信展示小程序的秘诀揭秘

随着微信的普及和发展&#xff0c;微信展示小程序成为了许多企业展示自己形象的重要渠道。那么如何快速地搭建一个精美的微信展示小程序呢&#xff1f;下面就为大家详细介绍一下具体操作步骤。 首先&#xff0c;进入【乔拓云】平台后台。乔拓云是一款非常优秀的小程序开发平台&…

php伪协议(文件包含)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 php伪协议 伪协议详情 php伪协议 文件包含直接读取的是文件&#xff0c;而不是文件源码&#xff0c;所以要想办法读取源码方法。 那么就要涉及到 PHP 伪协议 ph…

LabVIEW开发航空电子设备嵌入式诊断半物理仿真系统

LabVIEW开发航空电子设备嵌入式诊断半物理仿真系统 航电集成系统是现代战争飞机的重要组成部分&#xff0c;包括惯性导航系统、飞行控制系统、机电管理系统和任务计算机等子系统。战机的作战性能与航电系统息息相关&#xff0c;可以说&#xff0c;没有高性能的空电系统&#x…

行业首家!法大大荣获“数据安全管理能力认证(DSMC)”证书

7月11日&#xff0c;法大大获得由中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;颁发的“数据安全管理能力认证&#xff08;DSMC&#xff09;证书”&#xff08;以下简称“DSMC证书”&#xff09;&#xff0c;成为行业内首家获颁该证书的企业&#xff0c;法…

基于Java+SpringBoot+Vue前后端分离校园管理系统详细设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Spring Cloud Alibaba【创建支付服务生产者、创建服务消费者 、Dubbo和OpenFeign区别 、微服务接入OpenFeign】(二)

目录 分布式服务治理_创建支付服务生产者 分布式服务治理_创建服务消费者 服务调用_Dubbo和OpenFeign区别 服务调用_微服务接入OpenFeign 分布式服务治理_创建支付服务生产者 创建服务提供者工程cloud-provider-payment8001 POM文件引入依赖 <dependencies><…

MP4怎么转换为gif的格式?快试试这两个方法!

想要将MP4视频文件转换为GIF格式&#xff1f;不用担心&#xff0c;本文将为您介绍两种简单易行的方法&#xff1a;记灵在线工具和使用FFmpeg命令行工具。这些方法适用于不同的用户&#xff0c;无论您是喜欢在线工具还是偏向命令行操作&#xff0c;都能找到适合自己的方式。让我…

开源代码分享(8)—大规模电动汽车时空耦合双层优化调度(附matlab代码)

参考文献&#xff1a; [1]He L , Yang J , Yan J , et al. A bi-layer optimization based temporal and spatial scheduling for large-scale electric vehicles[J]. Applied Energy, 2016, 168(apr.15):179-192. DOI:10.1016/j.apenergy.2016.01.089 1.基本原理 1.1摘要 电…

Python模块基础

一、模块 模块可以看成是一堆函数的集合体。 一个py文件内部就可以放一堆函数&#xff0c;因此一个py文件就可以看成一个模块。 如果这个py文件的文件名为module.py&#xff0c;模块名则是module。 1、模块的四种形式 在Python中&#xff0c;总共有以下四种形式的模块&…

MySQL约束和查询

约束和查询 1. 约束1.1 约束类型1.2 常用的约束 2. 查询2.1 聚合查询2.1.1 聚合函数2.1.2 GROUP BY2.1.3 HAVING 2.2 联合查询2.2.1 内连接2.2.2 外连接 2.3 合并查询 1. 约束 1.1 约束类型 NOT NULL - 指示某列不能存储 NULL 值。UNIQUE - 保证某列的每行必须有唯一的值。DE…

TCP四次挥手过程

TCP 断开连接是通过四次挥手方式。 双方都可以主动断开连接&#xff0c;断开连接后主机中的「资源」将被释放&#xff0c; 刚开始双方都处于 establised 状态&#xff0c;假如是客户端先发起关闭请求&#xff0c;过程如下图&#xff1a; 第一次挥手&#xff1a;客户端打算关闭…

C++模拟实现位图和布隆过滤器(哈希)

目录 前言引入&#xff1a; 一、位图 1.1 位图概念 1.2 位图的实现 1.3 位图的应用 二、布隆过滤器 2.1 哈希的弊端 2.2 布隆过滤器概念 2.3 布隆过滤器的插入 2.4 布隆过滤器的查找 2.5 布隆过滤器的删除 2.6 布隆过滤器的模拟实现 2.7 布隆过滤器优缺点 三、…

CSS ::file-selector-button伪元素修改input上传文件按钮的样式

默认样式 修改后的样式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdev…

HTTP 请求走私漏洞(HTTP Request Smuggling)

一、什么是Http 请求走私漏洞&#xff1f; HTTP请求走私漏洞&#xff08;HTTP Request Smuggling&#xff09;是一种安全漏洞&#xff0c;利用了HTTP协议中请求和响应的解析和处理方式的不一致性。攻击者通过构造特定的恶意请求&#xff0c;以欺骗服务器和代理服务器&#xff0…

微信小程序-地图上的图标计算旋转值朝向经纬度计算

废话不多说&#xff0c;开整 // 参数为寄件人经纬度和收件人经纬度 // 根据寄收件人经纬度弧度π进行rotate旋转计算 const getRotate (po1, po2) > {if (!(po1 && po2)) return 0const lng_a po1.longitudeconst lat_a po1.latitudeconst lng_b po2.longitud…

pg谓词下推分析(一)

谓词下推分析&#xff08;一&#xff09; 本文基于pg13.8。 谓词下推阶段即是把约束条件下推对条件涉及的表上&#xff08;RelOptlnfo&#xff09;&#xff0c;其中同时会涉及到等价类的推导&#xff0c;及建立逻辑连接关系&#xff08;外连接的SpecialJoinInfo结构的创建与设…

apple pencil二代平替笔哪个好用?苹果平板平替笔排行

光是一款Apple Pencil&#xff0c;就卖到了接近一千多块&#xff0c;信息对于很多人来说都觉得太贵了。事实上&#xff0c;由于平替电容笔的性价比也非常高&#xff0c;因此它还是值得一试的。不管是学习&#xff0c;写作&#xff0c;还是绘画&#xff0c;这支平替电容笔&#…

力扣 135. 分发糖果

题目来源&#xff1a;https://leetcode.cn/problems/candy/description/ C题解&#xff08;来源代码随想录&#xff09;&#xff1a; 先从左往右比较&#xff0c;右边孩子评分比左边高就多发1颗糖&#xff0c;否则就只发1颗&#xff1b;再从右往左比较&#xff0c;左边孩子评分…

Element ui table展开行中,某些行需要展开,某些行不需要展开

1.templatetemplate里面对应的代码&#xff1a; <el-table :data"menuList" style"width: 100%" :row-class-name"isShowIcon"><el-table-column type"expand"><template slot-scope"props" v-if"prop…