我在高职教STM32——LCD液晶显示(3)

2024-06-23 22:52
文章标签 stm32 lcd 高职 液晶显示

本文主要是介绍我在高职教STM32——LCD液晶显示(3),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正因如此,才有了借助 CSDN 平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思的教学设计分享出来,主要面向广大师生朋友,单片机老鸟就略过吧。欢迎点赞+关注,各位的支持是本人持续输出的动力,多谢多谢!

        前边我们讲解了LED、按键和蜂鸣器的应用,这三类器件本身工作原理十分简单,因此我们的重点是放在STM32的GPIO上面。这一章我们来学习一下开发板配套的那块厚厚的液晶屏——LCD1602,聚焦的是这个器件本身的特点和工作时序。因此,我们需要熟读它的数据手册,因为手册里告诉了编程的要点、参数、时序等。阅读器件手册是做单片机和嵌入式开发必备的基本能力,我们就从这一章开始锻炼起来吧。为了不让篇幅太长,本章打算分四个部分来讲解,本文是第三部分。

【学习目标】

  1. 了解LCD1602的工作原理
  2. 掌握LCD1602的工作时序
  3. 领悟软件模拟时序的思路和方法

三、液晶静态显示实验

        本章的前两个部分花了不少篇幅,全方面的介绍了LCD1602以及与开发板之间的联系,传递出来的无非就是一个意思——吃透数据手册这别无他法,结合参考程序反复阅读手册,慢慢感悟,开发经验就是这么积累起来的。学完这个入门的液晶屏,后面还有更复杂的彩屏和触摸屏等着我们去学习,依然是“啃”数据手册。好了,下面我们就动手来写一个程序,把手册里的内容转换成代码,驱动LCD1602去显示我们想要的效果。

3.1 任务描述

        编写LCD1602驱动代码,上电之后可以在指定位置显示字符串信息,实验效果如图13所示。

图13 LCD1602静态显示实验效果

3.2 工程文件清单

        与之前的工程一样,控制一类新的硬件就增加一对与之匹配的驱动文件,即图14中的 lcd1602.clcd1602.h

图14 LCD1602工程文件

3.3 工程源码剖析

        这里为了突出源码的功能细节和排版之需,对源码进行了必要的分割处理。

3.3.1 lcd1602.h源码剖析

        该文件源码见代码清单4,主要是LCD1602端口操作的宏定义和驱动函数的声明,每个函数的功能和参数将在下面剖析 lcd1602.c 源码时解读。

//---------------------------------------------------------
// 代码清单4:lcd1602.h
//---------------------------------------------------------#ifndef _LCD1602_H_
#define _LCD1602_H_
#include "stm32f10x.h"//---------------------------------------------------------
// 端口操作宏定义
//---------------------------------------------------------
#define	 RS_H	GPIO_SetBits(GPIOC, GPIO_Pin_6)
#define  RS_L	GPIO_ResetBits(GPIOC, GPIO_Pin_6)
#define  RW_H	GPIO_SetBits(GPIOA, GPIO_Pin_11)
#define  RW_L	GPIO_ResetBits(GPIOA, GPIO_Pin_11)
#define  EN_H	GPIO_SetBits(GPIOB, GPIO_Pin_4)
#define  EN_L	GPIO_ResetBits(GPIOB, GPIO_Pin_4)
#define  READ_BUSY()	GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_2)//通过直接配置寄存器来改变PC2是输入还是输出
//读液晶状态时是输入,写命令和写数据时是输出
//GPIOx->CRL寄存器描述见手册8.2.1小节(P113)
#define PC2_OUT()	{GPIOC->CRL&=0xFFFFF0FF; GPIOC->CRL|=0x00000300;}
#define PC2_IN()	{GPIOC->CRL&=0xFFFFF0FF; GPIOC->CRL|=0x00000800;}//---------------------------------------------------------
// 驱动函数声明
//---------------------------------------------------------
_Bool Lcd1602_WaitReady(void);
void Lcd1602_SendByte(u8 byte);
void Lcd1602_WriteCmd(u8 byte);
void Lcd1602_WriteData(u8 byte);
void Lcd1602_ShowChar(u8 x, u8 y, u8 ch);
void Lcd1602_ShowStr(u8 x, u8 y, u8 *str);
void Lcd1602_Clear(u8 pos);
void Lcd1602_Init(void);
void Lcd1602_Printf(u8 x, u8 y, char *fmt, ...);#endif

3.3.2 lcd1602.c源码剖析

        该文件就是所有LCD1602驱动函数的定义,下面就逐个进行剖析。

        1) 头文件部分

        首先,把必要的头文件都加进来,如代码清单5所示。

/************************************************************************** 代码清单5:lcd1602.c的头文件* 描    述:LCD1602初始化、驱动* 平    台:麒麟座V3.2* 作    者:老耿* 日    期:2024-04-09* 固 件 库:ST3.5.0* 版    本:V1.0* 修改记录:无************************************************************************
*///必要的头文件
#include "delay.h"
#include "lcd1602.h"//C库
#include <stdarg.h>
#include <stdio.h

        2) Lcd1602_WaitReady()函数源码

        该函数就是用来检测液晶是否准备好,返回1表示“忙”,返回“0”表示“不忙”。详细源码见如下代码清单6。

/*
************************************************************
*	代码清单6:	Lcd1602_WaitReady()函数
*	函数功能:	等待液晶准备好
*	入口参数:	无
*	返回参数:	1:忙,0:不忙
*	说明:
************************************************************
*/
_Bool Lcd1602_WaitReady(void)
{PC2_IN();		//PC2输入模式RS_L;			//拉低RSRW_H;			//拉高RWEN_L;			//delay_us(1);	//EN高脉冲EN_H;			//return (_Bool)READ_BUSY();	//返回PC2状态
}

        2) Lcd1602_SendByte()函数源码

        该函数把一个字节(参数byte)送上液晶的8位数据端口,高3位送到PC2 ~ PC0,低5位送上PB9 ~ PB5。送数的过程如代码清单7所示,有一点曲折,但各位可以从中好好体会一下C语言位操作的严谨和奇妙。

/*
************************************************************
*	代码清单7:	Lcd1602_SendByte()函数
*	函数功能:	向LCD1602写一个字节
*	入口参数:	byte:需要写入的数据
*	返回参数:	无
*	说明:		
************************************************************
*/
void Lcd1602_SendByte(u8 byte)
{u16 value = 0;value = GPIO_ReadOutputData(GPIOB);		//读取GPIOB的数据value &= ~(0x001F << 5);				//清除bit5~8value |= ((u16)byte & 0x001F) << 5;		//将要写入的数据取低5位并左移5位GPIO_Write(GPIOB, value);				//写入GPIOBvalue = GPIO_ReadOutputData(GPIOC);		//读取GPIOC的数据value &= ~(0x0007 << 0);				//清除bit0~2value |= ((u16)byte & 0x00E0) >> 5;		//将要写入的数据取高3位并右移5位GPIO_Write(GPIOC, value);				//写入GPIOCdelay_us(10);
}

        首先,我们得清楚,要改变的只有PC2 ~ PC0、PB9 ~ PB5这8位,而这两组I/O的其他位是不能变的,因为其它I/O还连着别的硬件呢。所以,才有了先保存这组I/O的值。接下来,低5位的操作过程可以用图15来表示,这几句很好的诠释了C语言常见的位操作在嵌入式层面是如何应用的,希望各位能好好领悟。同理,高3位送到PC2~PC0,各位可以自己琢磨和推导一下。

图15 PB9~PB5的送数过程

        3) Lcd1602_WriteCmd()函数源码

        该函数实现写一个命令(参数byte)到LCD1602,就是按照数据手册上写命令的时序编写的,大家可以对照手册来阅读,源码见如下代码清单8。

/*
************************************************************
*	代码清单8:	Lcd1602_WriteCmd()函数
*	函数功能:	向LCD1602写命令
*	入口参数:	byte:需要写入的命令
*	返回参数:	无
*	说明:
************************************************************
*/
void Lcd1602_WriteCmd(u8 byte)
{while(Lcd1602_WaitReady());		//等到不忙PC2_OUT();						//PC2输出模式RS_L;							//拉低RSRW_L;							//拉低RWLcd1602_SendByte(byte);			//准备命令码EN_H;							//拉高使能delay_us(20);					//保持一定时间EN_L;							//拉低使能delay_us(5);
}

        4) Lcd1602_WriteData()函数源码

        该函数与写命令函数是一个套路,就是通过拉高RS改成了数据模式,源码见代码清单9。

/*
************************************************************
*	代码清单9:	Lcd1602_WriteData()
*	函数功能:	向LCD1602写数据
*	入口参数:	byte:需要写入的数据
*	返回参数:	无
*	说明:
************************************************************
*/
void Lcd1602_WriteData(u8 byte)
{while(Lcd1602_WaitReady());		//等到不忙PC2_OUT();						//PC2输出模式RS_H;							//拉高RSRW_L;							//拉低RWLcd1602_SendByte(byte);			//准备数据EN_H;							//拉高使能delay_us(20);					//保持一定时间EN_L;							//拉低使能delay_us(5);
}

        5) Lcd1602_SetCursor()函数源码

        该函数用来设置光标的位置,参数x和y是位置坐标,x是行坐标(0表示第一行,1表示第二行),y是列坐标(0~15),源码见如下代码清单10。

/*
************************************************************
*	代码清单10:	Lcd1602_SetCursor()函数
*	函数功能:	设置显示RAM地址地址,即光标位置
*	入口参数:	x:行坐标(0第一行,1第二行)
*				y:列坐标(0~15)
*	返回参数:	无
*	说明:
************************************************************
*/
void Lcd1602_SetCursor(u8 x, u8 y)
{u8 addr;if(x==0)				//第一行addr = 0x00 + y;else					//第二行addr = 0x40 + y;Lcd1602_WriteCmd(addr|0x80);	//写入地址
}

        6) Lcd1602_ShowChar()函数源码

        该函数用来显示单个字符,参数x和y与上面一样,确定在哪个位置显示,ch为字符内容,源码见如下代码清单11。

/*
************************************************************
*	代码清单11:	Lcd1602_ShowChar()函数
*	函数功能:	在液晶上显示单个字符
*	入口参数:	x和y:显示的坐标(同上)
*				ch:待显示的字符
*	返回参数:	无
*	说明:
************************************************************
*/
void Lcd1602_ShowChar(u8 x, u8 y, u8 ch)
{Lcd1602_SetCursor(x, y);	//设置坐标Lcd1602_WriteData(ch);		//显示字符
}

        7) Lcd1602_ShowStr()函数源码

        该函数用来显示字符串信息,参数x和y与上面一样,确定从哪个位置开始显示,*str指向待显示的字符串空间,源码见如下代码清单12。

/*
************************************************************
*	代码清单12:	Lcd1602_ShowStr()函数
*	函数功能:	在液晶上显示字符串
*	入口参数:	x和y:显示的起始坐标(同上)
*				str:字符串指针
*	返回参数:	无
*	说明:
************************************************************
*/
void Lcd1602_ShowStr(u8 x, u8 y, u8 *str)
{Lcd1602_SetCursor(x, y);//每写完一个字符,光标会自动指向下一个位置while(*str)		//字符串没结束就不停{Lcd1602_WriteData(*str);	//写入当前字符str++;						//指向下一个字符}
}

        8) Lcd1602_Clear()函数源码

        该函数用来清屏,参数pos可取值为0、1、2,分别表示清除第一行、第二行和整屏,源码见如下代码清单13。

/*
************************************************************
*	代码清单13:	Lcd1602_Clear()函数
*	函数功能:	LCD1602清除指定行
*	入口参数:	pos:指定的行
*	返回参数:	无
*	说明:		0-第一行		1-第二行		2-两行
************************************************************
*/
void Lcd1602_Clear(u8 pos)
{switch(pos){case 0:Lcd1602_ShowStr(0, 0 , "                ");break;case 1:Lcd1602_ShowStr(1, 0 , "                ");break;		case 2:Lcd1602_WriteCmd(0x01);	//清屏命令break;default:break;}
}

        9) Lcd1602_Init()函数源码

        该函数完成LCD1602上电之后的初始化,一方面将所连接的I/O口全部初始化,另一方面按照数据手册交待的复位步骤对液晶进行初始化,源码见如下代码清单14。

/*
************************************************************
*	代码清单14:	Lcd1602_Init()函数
*	函数功能:	LCD1602初始化
*	入口参数:	无
*	返回参数:	无
*	说明:		RW-PA11		RS-PC6		EN-PC3
*				D7~D5 - PC2~PC0		D4~D0 - PB9~PB5
************************************************************
*/
void Lcd1602_Init(void)
{GPIO_InitTypeDef gpio_initstruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | \RCC_APB2Periph_GPIOB | \RCC_APB2Periph_GPIOC, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);	//禁止JTAG功能gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_PP;gpio_initstruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | \GPIO_Pin_6 | GPIO_Pin_7 | \GPIO_Pin_8 | GPIO_Pin_9;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &gpio_initstruct);gpio_initstruct.GPIO_Pin = GPIO_Pin_11;GPIO_Init(GPIOA, &gpio_initstruct);gpio_initstruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | \GPIO_Pin_2 | GPIO_Pin_6;GPIO_Init(GPIOC, &gpio_initstruct);Lcd1602_WriteCmd(0x38);	//16*2显示,5*7点阵,8位数据接口Lcd1602_WriteCmd(0x0C);	//开显示,光标关闭Lcd1602_WriteCmd(0x06);	//字符不动,光标移动Lcd1602_WriteCmd(0x01);	//清屏
}

        LCD1602液晶手册提供了一个初始化过程,由于不检测“忙”位,所以程序比较复杂,如图16所示。而我们编写的程序已经将检测“忙”位的功能嵌入到写操作里面了,所以只用了最后4行语句就完成了同样效果,更加简易方便。手册上描述的那个,大家仅作了解即可。以后在别的资料里看到了与我们这类不一样的初始化也不要困惑,注意跟我们这里联系和对比。

图16 数据手册上的初始化过程

3.3.3 main.c源码剖析

        主程序比较简单,完成初始化之后就调用显示函数在屏上指定的位置显示指定的字符串,源码见如下代码清单15。

/********************************************************* 代码清单15:main.c* 项    目:LCD1602液晶显示* 任务描述:静态显示* 实验平台:OneNET STM32开发板V3.2* 作    者:老耿* 日    期:yyyy/mm/dd******************************************************
**///-----------------------------------------------------
// 必要的头文件
//-----------------------------------------------------
#include "delay.h"
#include "lcd1602.h"int main()
{delay_init();				//Systick初始化,用于普通的延时Lcd1602_Init();				//LCD1602初始化Lcd1602_ShowStr(0, 3, "KylinV3.2");		//第一行第4个字符开始显示字符串Lcd1602_ShowStr(1, 2, "STM32 Board");	//第二行第3个字符开始显示字符串while(1);
}

3.3.4 验证与测试

        程序下载前,接好液晶屏和电源适配器,并将电源拨动开关置于OFF处(如图17所示)。程序下载后,电源开关拨至ON,即可实现实验效果。

图17 实验效果与电源拨动开关

(第三部分完,共四部分) 

这篇关于我在高职教STM32——LCD液晶显示(3)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1088506

相关文章

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

STM32内部闪存FLASH(内部ROM)、IAP

1 FLASH简介  1 利用程序存储器的剩余空间来保存掉电不丢失的用户数据 2 通过在程序中编程(IAP)实现程序的自我更新 (OTA) 3在线编程(ICP把整个程序都更新掉) 1 系统的Bootloader写死了,只能用串口下载到指定的位置,启动方式也不方便需要配置BOOT引脚触发启动  4 IAP(自己写的Bootloader,实现程序升级) 1 比如蓝牙转串口,

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

寻迹模块TCRT5000的应用原理和功能实现(基于STM32)

目录 概述 1 认识TCRT5000 1.1 模块介绍 1.2 电气特性 2 系统应用 2.1 系统架构 2.2 STM32Cube创建工程 3 功能实现 3.1 代码实现 3.2 源代码文件 4 功能测试 4.1 检测黑线状态 4.2 未检测黑线状态 概述 本文主要介绍TCRT5000模块的使用原理,包括该模块的硬件实现方式,电路实现原理,还使用STM32类

STM32 ADC+DMA导致写FLASH失败

最近用STM32G070系列的ADC+DMA采样时,遇到了一些小坑记录一下; 一、ADC+DMA采样时进入死循环; 解决方法:ADC-dma死循环问题_stm32 adc dma死机-CSDN博客 将ADC的DMA中断调整为最高,且增大ADCHAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_Buffer_Size); 的ADC_Bu

基于stm32的河流检测系统-单片机毕业设计

文章目录 前言资料获取设计介绍功能介绍具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机设计精品

STM32的使用方法一

注:我采用的是STM32F103RC芯片、相应的电路图和STM32CubeIDE软件这是在STM32CubeIDE软件定义芯片后,所给的必要的代码逻辑,加上了注释 #include "main.h"/* Private variables ---------------------------------------------------------*//* Private function

STM32 HAL CAN通讯 实操

1、简介 相比于串口通讯,对于刚接触CAN通讯的小白来说,CAN通讯相对复杂,看各种视频、帖子理论,总是一知半解。本次通过傻瓜式操作,先实现CAN通讯的交互,以提高小白的信心,也便于自己复习观看。本次以STM32CubeMX进行初始化配置,通过Keil 5软件进行软件设计,通过CAN盒实现进行数据的交互。该流程实际以STM32F0、F1、F3、F4、F7实测好用(理论上都适用),这三种型号单片机

我在高职教STM32——准备HAL库工程模板(1)

新学期开学在即,又要给学生上 STM32 嵌入式课程了。这课上了多年了,一直用的都是标准库来开发,已经驾轻就熟了。人就是这样,有了自己熟悉的舒适圈,就很难做出改变,老师上课也是如此,排斥新课和不熟悉的内容。显然,STM32 的开发,HAL 库已是主流,自己其实也在使用,只不过更换库就意味着教学内容有很大变化,自己也就迟迟没有迈出调整这一步。现在,是时候做出变化了,笔者计划保持教学项