【Proteus】:LCD1602仿真实验基于STM32CubeMX

前言

时不可以苟遇,道不可以虚行。


一、LCD1602介绍

  • LCD1602 是一种工业字符型液晶,能够同时显示 16x0232 个字符。LCD1602液晶显示原理: LCD1602液晶显示的原理是利用液晶的物理特性,通过电压对其显示区域进行控制,有电就有显示,这样即可以显示出图形。1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。
  • LCD1602 则是指显示内容为 16 * 2 ,即可以显示两行、每行可以显示 16 个字符的液晶模块。

1、引脚说明:

  • 下图是在 proteus 中的 LCD1602 屏幕的样子,少了两个背光引脚:
引脚功能
VSS地电源
VDD接 5 V 电源
VEE/VL液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。
RS指令/数据选择信号,高电平时选择数据寄存器、低电平时选择指令寄存器
RW读写信号线,高电平时进行读操作,低电平时进行写操作
E使能端,当E端由高电平跳变成低电平时,液晶模块执行命令
D0~D7双向数据线,传输信息
BLA背光源正极
BLK背光源负极
  • 在这里插入图片描述

2、指令集

在这里插入图片描述

在这里插入图片描述


二、工程建立

准备:

  • proteus 软件:proteus 8.13
  • STM32CubeMX 软件:6.5
  • Keil MDK

1、新建仿真工程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 所需元器件查找:
元器件符号
LCD1602LM016L
电位器(滑动变阻器)POT
STM32单片机STM32F103R6
  • 仿真连线如下图所示:

在这里插入图片描述

  • 供电网配置:
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2、STM32cubemx新建工程

1、 选择芯片型号:

在这里插入图片描述

2、时钟配置

在这里插入图片描述
在这里插入图片描述

3、引脚输出配置

在这里插入图片描述

4、生成代码

在这里插入图片描述

在这里插入图片描述

三、编写代码

在这里插入图片描述

  • 在 main.c 中:
/* USER CODE BEGIN Includes */

#include  "LCD1602.h"

/* USER CODE END Includes */
/* USER CODE BEGIN 2 */

	LCD_Init();
	LCD_Puts(0,0,"I am successful.");

/* USER CODE END 2 */
  • LCD1602.h:
#ifndef __LCD1602_H_
#define __LCD1602_H_

#include "main.h"

#define _LCD_COLS         16
#define _LCD_ROWS         2

/*****  GPIOB3  H:data;L:command  ******/
#define  LCD1602_RS_DATA	   	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_SET)		
#define  LCD1602_RS_CMD			HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_RESET)
/*****  GPIOB4  H:read;L:write  ******/
#define  LCD1602_RW_READ        HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET)
#define  LCD1602_RW_WRITE       HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET)
/*****  GPIOB5    ******/
#define  LCD1602_E_HIGH         HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET)
#define  LCD1602_E_LOW          HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET)

#define  LCD1602_DATA(x)				GPIOB->ODR=(GPIOB->ODR & 0x00ff) | (x<<8)

/* 指令集 */
#define LCD_CLEARDISPLAY        0x01	//清屏指令
#define LCD_RETURNHOME          0x02	//地址计数器 AC=0;(此时地址为 0x80) 光标归原点,但是 DDRAM 中断内容不变
#define LCD_ENTRYMODESET        0x04	//输入模式设置
#define LCD_DISPLAYCONTROL      0x08	//只开显示
#define LCD_CURSORSHIFT         0x10	
#define LCD_FUNCTIONSET         0x20
#define LCD_SETCGRAMADDR        0x40
#define LCD_SETDDRAMADDR        0x80

/* 显示入口模式的标志 */
#define LCD_ENTRYRIGHT          0x00
#define LCD_ENTRYLEFT           0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00
/* 开关控制标志 */
#define LCD_DISPLAYON           0x04
#define LCD_CURSORON            0x02
#define LCD_BLINKON             0x01

#define LCD_DISPLAYMOVE         0x08
#define LCD_CURSORMOVE          0x00
#define LCD_MOVERIGHT           0x04
#define LCD_MOVELEFT            0x00

#define LCD_8BITMODE            0x10
#define LCD_4BITMODE            0x00
#define LCD_2LINE               0x08
#define LCD_1LINE               0x00
#define LCD_5x10DOTS            0x04
#define LCD_5x8DOTS             0x00

typedef struct {
uint8_t DisplayControl;
uint8_t DisplayFunction;
uint8_t DisplayMode;
uint8_t currentX;
uint8_t currentY;
} LCD_Options_t;

void LCD_Init(void);	//屏幕初始化	
void LCD_DisplayOn(void);	
void LCD_DisplayOff(void);
void LCD_Clear(void);	//清屏
void LCD_Puts(uint8_t x, uint8_t y, char* str);		//显示字符串
void LCD_BlinkOn(void);
void LCD_BlinkOff(void);
void LCD_CursorOn(void);
void LCD_CursorOff(void);
void LCD_ScrollLeft(void);
void LCD_ScrollRight(void);
void LCD_CreateChar(uint8_t location, uint8_t* data);
void LCD_PutCustom(uint8_t x, uint8_t y, uint8_t location);
void LCD_Put(uint8_t Data);

#endif
  • LCD1602.c:
#incldue “LCD1602.h”

static void LCD_Cmd(uint8_t cmd);
static void LCD_Data(uint8_t data);
static void LCD_CursorSet(uint8_t col, uint8_t row);
static void LCD_EnBlink(void);


static LCD_Options_t LCD_Opts;

//us级Delay
void  LCD_Delay_us(uint16_t  us)
{
    uint32_t  Div = (SysTick->LOAD+1)/1000;
    uint32_t  StartMicros = HAL_GetTick()*1000 + (1000- SysTick->VAL/Div);
    while((HAL_GetTick()*1000 + (1000-SysTick->VAL/Div)-StartMicros < us));
}
//ms级Delay
void  LCD_Delay_ms(uint8_t  ms)
{
    HAL_Delay(ms);
}
//LCD初始化
void LCD_Init(void)
{
  /* Set cursor pointer to beginning for LCD */
    LCD_Opts.currentX = 0;
    LCD_Opts.currentY = 0;
    LCD_Opts.DisplayFunction = LCD_8BITMODE | LCD_5x8DOTS | LCD_1LINE;
    if (_LCD_ROWS > 1)
    LCD_Opts.DisplayFunction |= LCD_2LINE;
  /* Set # lines, font size,8BITMODE,etc. */
    LCD_Cmd(LCD_FUNCTIONSET | LCD_Opts.DisplayFunction);
  /* Turn the display on with no cursor or blinking default */
    LCD_Opts.DisplayControl = LCD_DISPLAYON;
    LCD_DisplayOn();
    /* Default font directions 文字不动,地址自动+1*/
    LCD_Opts.DisplayMode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
    LCD_Cmd(LCD_ENTRYMODESET | LCD_Opts.DisplayMode);
    LCD_Clear();
    LCD_Delay_ms(5);
}
//清屏
void LCD_Clear(void)
{
    LCD_Cmd(LCD_CLEARDISPLAY);
    LCD_Delay_ms(5);
}
//显示一个string,支持换行
void LCD_Puts(uint8_t x, uint8_t y, char* str)
{
    LCD_CursorSet(x, y);
    while (*str) {
        if (LCD_Opts.currentX >= _LCD_COLS)/*移动到下一行*/ {
            LCD_Opts.currentX = 0;
            LCD_Opts.currentY++;
            LCD_CursorSet(LCD_Opts.currentX, LCD_Opts.currentY);
        }
        if (*str == '\n') /*换行,列对其*/{
            LCD_Opts.currentY++;
            LCD_CursorSet(LCD_Opts.currentX, LCD_Opts.currentY);
        } else if (*str == '\r') /*回车,换行回行首*/{
            LCD_CursorSet(0, LCD_Opts.currentY);
        } else {
            LCD_Data(*str);
            LCD_Opts.currentX++;
        }
        str++;
    }
}

void LCD_DisplayOn(void)
{
    LCD_Opts.DisplayControl |= LCD_DISPLAYON;
    LCD_Cmd(LCD_DISPLAYCONTROL | LCD_Opts.DisplayControl);
}

void LCD_DisplayOff(void)
{
    LCD_Opts.DisplayControl &= ~LCD_DISPLAYON;
    LCD_Cmd(LCD_DISPLAYCONTROL | LCD_Opts.DisplayControl);
}

void LCD_BlinkOn(void)
{
    LCD_Opts.DisplayControl |= LCD_BLINKON;
    LCD_Cmd(LCD_DISPLAYCONTROL | LCD_Opts.DisplayControl);
}

void LCD_BlinkOff(void)
{
    LCD_Opts.DisplayControl &= ~LCD_BLINKON;
    LCD_Cmd(LCD_DISPLAYCONTROL | LCD_Opts.DisplayControl);
}

void LCD_CursorOn(void)
{
    LCD_Opts.DisplayControl |= LCD_CURSORON;
    LCD_Cmd(LCD_DISPLAYCONTROL | LCD_Opts.DisplayControl);
}

void LCD_CursorOff(void)
{
    LCD_Opts.DisplayControl &= ~LCD_CURSORON;
    LCD_Cmd(LCD_DISPLAYCONTROL | LCD_Opts.DisplayControl);
}

void LCD_ScrollLeft(void)
{
    LCD_Cmd(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}

void LCD_ScrollRight(void)
{
    LCD_Cmd(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

void LCD_CreateChar(uint8_t location, uint8_t *data)
{
    uint8_t i;
  /* We have 8 locations available for custom characters */
    location &= 0x07;
    LCD_Cmd(LCD_SETCGRAMADDR | (location << 3));

    for (i = 0; i < 8; i++) {
        LCD_Data(data[i]);
    }
}

void LCD_PutCustom(uint8_t x, uint8_t y, uint8_t location)
{
    LCD_CursorSet(x, y);
    LCD_Data(location);
}

static void LCD_EnBlink(void)
{
    LCD1602_E_HIGH;
    LCD_Delay_us(50);
    LCD1602_E_LOW;
    LCD_Delay_us(50);
}

static void LCD_Cmd(uint8_t cmd)
{
    LCD1602_RS_CMD;
    LCD1602_RW_WRITE;
    LCD1602_DATA(cmd);
    LCD_EnBlink();
}

static void LCD_Data(uint8_t data)
{
    LCD1602_RS_DATA;
    LCD1602_RW_WRITE;
    LCD1602_DATA(data);
    LCD_EnBlink();
}

static void LCD_CursorSet(uint8_t col, uint8_t row)
{
    uint8_t row_offsets[] = {0x00, 0x40, 0x14, 0x54};
    if (row >= _LCD_ROWS)
        row = 0;
    LCD_Opts.currentX = col;
    LCD_Opts.currentY = row;
    LCD_Cmd(LCD_SETDDRAMADDR | (col + row_offsets[row]));
}

void LCD_Put(uint8_t Data)
{
    LCD_Data(Data);
}

四、结果

在这里插入图片描述

### STM32LCD1602的仿真教程 #### 连接方法 为了使STM32F103主控芯片成功驱动LCD1602显示屏,需按照特定的方式连接两者之间的引脚。通常情况下,LCD1602的数据线(D4-D7)、RS、E以及RW引脚需要分别接到STM32的不同GPIO端口上。具体来说: - RS (寄存器选择): GPIOA, Pin 9 - E (使能信号): GPIOA, Pin 8 - D4: GPIOA, Pin 2 - D5: GPIOA, Pin 3 - D6: GPIOA, Pin 4 - D7: GPIOA, Pin 5 - RW (读写控制), 接地设置为只写模式[^1] 这种四位接口方式减少了所需的I/O资源数量,同时保持了足够的性能来操作液晶屏。 #### 示例代码 下面给出一段用于初始化和显示字符到LCD1602屏幕上的简单C语言函数实现。这段代码假设已经完成了必要的硬件配置工作,并且库文件包含了标准外设驱动程序的支持。 ```c #include "stm32f1xx_hal.h" #define LCD_E_PIN GPIO_PIN_8 #define LCD_RS_PIN GPIO_PIN_9 #define DATA_PORT GPIOA void lcd_write_command(uint8_t cmd); void lcd_init(void); // 初始化LCD1602 void lcd_init() { HAL_Delay(15); // Wait for power up lcd_write_command(0x33); // Function set: 4-bit mode, 2-line display, 5×7 font size HAL_Delay(5); lcd_write_command(0x32); // Repeat function set command to ensure it's recognized correctly HAL_Delay(5); lcd_write_command(0x28); // Set interface length to 4 bits and other parameters as before HAL_Delay(5); lcd_write_command(0x0C); // Display on/off control: cursor off, blink off HAL_Delay(5); lcd_write_command(0x06); // Entry mode set: increment address counter after each character written HAL_Delay(5); } // 向LCD发送指令 void lcd_write_command(uint8_t cmd) { uint8_t high_nibble = cmd & 0xF0; uint8_t low_nibble = (cmd << 4) & 0xF0; // Send high nibble first HAL_GPIO_WritePin(DATA_PORT, HIGH_NIBBLE_PINS, high_nibble >> 4); HAL_GPIO_WritePin(GPIOA, LCD_RS_PIN, RESET); // Command register select HAL_GPIO_TogglePin(GPIOA, LCD_E_PIN); // Toggle enable pin HAL_Delay(1); // Then send the lower nibble HAL_GPIO_WritePin(DATA_PORT, LOW_NIBBLE_PINS, low_nibble >> 4); HAL_GPIO_TogglePin(GPIOA, LCD_E_PIN); // Again toggle enable pin } ``` 此段代码展示了如何利用HAL库中的`HAL_GPIO_WritePin()`函数向指定的GPIO引脚输出高低电平,从而完成对LCD1602的操作命令传输过程。 #### 使用Proteus进行仿真 对于想要验证上述电路设计及软件逻辑的同学,可以考虑采用Proteus这样的电子线路模拟工具来进行虚拟环境下的测试。在Proteus ISIS中添加相应的元件模型——即STM32微控制器和LCD1602模块之后,便可以根据实际物理连线情况建立好整个系统的原理图;接着加载编译好的固件映像至MCU内部Flash存储区,启动运行即可观察预期效果。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

W_oilpicture

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值