STM32 使用LCD1602简单显示字符

一、概述

  1. 功能说明

本驱动适配 LCD1602 8 位并行模式,完全对标原 51 单片机逻辑迁移至 STM32,实现 LCD 初始化、清屏、指定坐标字符串打印;硬件引脚固定 PB 组,底层采用标准库 GPIO 操作,不依赖 HAL 库。

  1. 硬件引脚映射

表格

LCD1602 信号 STM32 引脚 原 51 引脚 功能

RS PB1 P2.6 寄存器选择:0 = 命令,1 = 数据

RW PB2 P2.5 读写选择:0 = 写,1 = 读

E PB0 P2.7 使能时钟,上升沿锁存数据

D0~D7 PB8~PB15 P0.0~P0.7 8 位并行数据总线

  1. 编译环境

芯片:STM32F10x(大容量 / 中容量通用)

编译器:Keil ARM V5

驱动库:STM32F10x_StdPeriph_Driver(标准库)

通信模式:默认 8 位并行(可宏切换 4 位模式)

二、文件结构

lcd1602.h:宏定义、引脚配置、函数声明

lcd1602.c:GPIO 初始化、底层读写、业务功能实现

三、头文件 lcd1602.h 说明

  1. 核心宏定义

c

运行

// 接口模式 0=8位并口,1=4位并口

#define LCD1602_4OR8_DATA_INTERFACE 0

// 控制端口GPIOB

#define LCD_RS_PIN GPIO_Pin_1

#define LCD_RW_PIN GPIO_Pin_2

#define LCD_E_PIN GPIO_Pin_0

#define LCD_CTRL_PORT GPIOB

#define LCD_CTRL_RCC RCC_APB2Periph_GPIOB

// 数据端口 PB8~PB15

#define LCD_DATA_MASK GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15

#define LCD_DATA_PORT GPIOB

#define LCD_DATA_RCC RCC_APB2Periph_GPIOB

  1. 电平操作宏

简化引脚高低电平控制,替代重复GPIO_SetBits/ResetBits

c

运行

#define LCD_RS_H() GPIO_SetBits(LCD_CTRL_PORT, LCD_RS_PIN)

#define LCD_RS_L() GPIO_ResetBits(LCD_CTRL_PORT, LCD_RS_PIN)

#define LCD_RW_H() GPIO_SetBits(LCD_CTRL_PORT, LCD_RW_PIN)

#define LCD_RW_L() GPIO_ResetBits(LCD_CTRL_PORT, LCD_RW_PIN)

#define LCD_E_H() GPIO_SetBits(LCD_CTRL_PORT, LCD_E_PIN)

#define LCD_E_L() GPIO_ResetBits(LCD_CTRL_PORT, LCD_E_PIN)

  1. 对外 API 声明

c

运行

void lcd1602_gpio_init(void); // LCD引脚GPIO初始化

void lcd1602_write_cmd(uint8_t cmd); // 写指令

void lcd1602_write_data(uint8_t dat);// 写显示数据

void lcd1602_init(void); // LCD整机初始化

void lcd1602_clear(void); // LCD清屏

void lcd1602_show_string(uint8_t x, uint8_t y, uint8_t *str); // 指定坐标显示字符串

void delay_ms(uint32_t ms); // 毫秒延时(文件内置简易循环延时)

四、源文件 lcd1602.c 功能详解

  1. 延时函数 delay_ms

72MHz 系统时钟简易阻塞式毫秒延时,仅用于 LCD 时序等待;若工程存在全局SysTick/delay.c延时,需删除本函数避免multiply defined链接报错。

c

运行

void delay_ms(uint32_t ms)

{

uint32_t i,j;

for(i = ms; i > 0; i--)

for(j = 7200; j > 0; j--);

}

  1. GPIO 初始化 lcd1602_gpio_init

开启 GPIOB 外设时钟;

PB0/PB1/PB2(RS/RW/E)配置 50MHz 推挽输出;

PB8~PB15 数据总线配置 50MHz 推挽输出;

上电默认拉低 RS、RW、E,空闲状态。

  1. 底层数据输出静态函数 lcd_set_dataport

适配硬件 D0 接 PB8、D7 接 PB15 的引脚布局,将单字节 8 位数据左移 8 位输出到 PB8~PB15:

c

运行

static void lcd_set_dataport(uint8_t val)

{

LCD_DATA_PORT->ODR &= ~(0xFF << 8); // 清空PB8~PB15

LCD_DATA_PORT->ODR |= ((uint16_t)val << 8); // 数据移位输出

}

  1. 底层写指令 lcd1602_write_cmd

RS=0:选择指令寄存器,RW=0 写模式;

E 引脚产生上升沿锁存指令;

8 位模式直接输出完整字节;4 位模式分两次输出高 4 位、低 4 位。

  1. 底层写数据 lcd1602_write_data

RS=1:选择数据寄存器,RW=0 写模式;

时序逻辑与写命令一致,用于输出 ASCII 显示字符。

  1. LCD 初始化 lcd1602_init

上电初始化标准流程(等待 20ms 稳定):

8 位模式指令:0x38(2 行 5*7 点阵);4 位模式0x28

0x0C:开启显示,关闭光标

0x06:写入字符后光标右移,屏幕不滚动

0x01:清屏

  1. 清屏 lcd1602_clear

发送指令0x01,清空屏幕所有字符,光标复位至第一行首位。

  1. 字符串显示 lcd1602_show_string

参数规则:

x:横向坐标 0~15;y:纵向坐标 0/1(两行屏幕)

自动换行:字符串超出本行 16 字符,自动切换至另一行输出

边界校验:y>1 或 x>15 直接退出,防止地址越界

五、调用示例 main.c

c

运行

#include "lcd1602.h"

int main(void)

{

lcd1602_init(); // 初始化LCD与GPIO

// 第一行0坐标打印

lcd1602_show_string(0,0,(uint8_t*)"PB LCD1602 8bit");

// 第二行1坐标打印

lcd1602_show_string(1,1,(uint8_t*)"StdLib No HAL");

while(1)

{

// 主循环

}

}

六、编译报错解决方案

  1. L6200E delay_ms multiply defined

故障:lcd1602.c内置延时与工程 SysTick/delay.c 延时重定义

解决:删除lcd1602.c内delay_ms函数,在lcd1602.h引入全局延时头文件#include "Public/SysTick.h",移除头文件内void delay_ms(uint32_t ms);声明。

  1. #1-D last line of file ends without a newline

故障:文件末尾无空行,Keil 警告

解决:所有.h/.c文件末尾回车新增一行空白再保存。

  1. L6218E Undefined symbol delay_10us

故障:按键驱动调用微秒延时,本驱动无该函数

解决:在 SysTick 驱动中补充delay_10us函数实现与声明,按键头文件包含 SysTick.h。

七、移植修改说明

如需更换硬件引脚,仅修改lcd1602.h内引脚宏:

控制端口:LCD_CTRL_PORT、LCD_RS_PIN、LCD_RW_PIN、LCD_E_PIN

数据端口:LCD_DATA_PORT、LCD_DATA_MASK、LCD_DATA_RCC

切换 4 位并行:修改宏#define LCD1602_4OR8_DATA_INTERFACE 1

八、详细代码

cpp 复制代码
#include "lcd1602.h"

// 72MHz简易ms延时
void delay_ms(uint32_t ms)
{
    uint32_t i,j;
    for(i = ms; i > 0; i--)
        for(j = 7200; j > 0; j--);
}

// IO初始化
void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef gpio_cfg;
    RCC_APB2PeriphClockCmd(LCD_CTRL_RCC, ENABLE);

    // 控制脚 PB0 PB1 PB2 推挽输出
    gpio_cfg.GPIO_Pin = LCD_E_PIN | LCD_RS_PIN | LCD_RW_PIN;
    gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_CTRL_PORT, &gpio_cfg);

    // 数据脚 PB8~PB15 推挽输出
    gpio_cfg.GPIO_Pin = LCD_DATA_MASK;
    gpio_cfg.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio_cfg.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_DATA_PORT, &gpio_cfg);

    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
}

// 将8位数据输出到PB8~PB15(D0→PB8,D7→PB15)
static void lcd_set_dataport(uint8_t val)
{
    // 先清除PB8~PB15
    LCD_DATA_PORT->ODR &= ~(0xFF << 8);
    // val低8位移到PB8~PB15输出
    LCD_DATA_PORT->ODR |= ((uint16_t)val << 8);
}

// 写命令
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_write_cmd(uint8_t cmd)
{
    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(cmd);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#else
void lcd1602_write_cmd(uint8_t cmd)
{
    LCD_RS_L();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(cmd >> 4);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();

    lcd_set_dataport(cmd & 0x0F);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#endif

// 写数据
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_write_data(uint8_t dat)
{
    LCD_RS_H();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(dat);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#else
void lcd1602_write_data(uint8_t dat)
{
    LCD_RS_H();
    LCD_RW_L();
    LCD_E_L();
    lcd_set_dataport(dat >> 4);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();

    lcd_set_dataport(dat & 0x0F);
    delay_ms(1);
    LCD_E_H();
    delay_ms(1);
    LCD_E_L();
}
#endif

// LCD初始化
#if (LCD1602_4OR8_DATA_INTERFACE == 0)
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    delay_ms(20);
    lcd1602_write_cmd(0x38);
    lcd1602_write_cmd(0x0C);
    lcd1602_write_cmd(0x06);
    lcd1602_write_cmd(0x01);
}
#else
void lcd1602_init(void)
{
    lcd1602_gpio_init();
    delay_ms(20);
    lcd1602_write_cmd(0x28);
    lcd1602_write_cmd(0x0C);
    lcd1602_write_cmd(0x06);
    lcd1602_write_cmd(0x01);
}
#endif

// 清屏
void lcd1602_clear(void)
{
    lcd1602_write_cmd(0x01);
}

// 显示字符串 x:0~15 y:0/1
void lcd1602_show_string(uint8_t x, uint8_t y, uint8_t *str)
{
    uint8_t i = 0;
    if(y > 1 || x > 15) return;

    if(y == 0)
    {
        while(*str != '\0')
        {
            if(i < 16 - x)
                lcd1602_write_cmd(0x80 + i + x);
            else
                lcd1602_write_cmd(0x80 + 0x40 + i + x - 16);
            lcd1602_write_data(*str);
            str++;
            i++;
        }
    }
    else
    {
        while(*str != '\0')
        {
            if(i < 16 - x)
                lcd1602_write_cmd(0x80 + 0x40 + i + x);
            else
                lcd1602_write_cmd(0x80 + i + x - 16);
            lcd1602_write_data(*str);
            str++;
            i++;
        }
    }
}




#ifndef _LCD1602_H
#define _LCD1602_H

#include "stm32f10x.h"

// 0=8位并口  1=4位并口
#define LCD1602_4OR8_DATA_INTERFACE 0

// 控制引脚映射
#define LCD_RS_PIN     GPIO_Pin_1
#define LCD_RW_PIN     GPIO_Pin_2
#define LCD_E_PIN      GPIO_Pin_0
#define LCD_CTRL_PORT  GPIOB
#define LCD_CTRL_RCC   RCC_APB2Periph_GPIOB

// 数据引脚 PB8~PB15 对应D0~D7
#define LCD_DATA_MASK  GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15
#define LCD_DATA_PORT  GPIOB
#define LCD_DATA_RCC   RCC_APB2Periph_GPIOB

// 控制电平宏
#define LCD_RS_H()     GPIO_SetBits(LCD_CTRL_PORT, LCD_RS_PIN)
#define LCD_RS_L()     GPIO_ResetBits(LCD_CTRL_PORT, LCD_RS_PIN)
#define LCD_RW_H()     GPIO_SetBits(LCD_CTRL_PORT, LCD_RW_PIN)
#define LCD_RW_L()     GPIO_ResetBits(LCD_CTRL_PORT, LCD_RW_PIN)
#define LCD_E_H()      GPIO_SetBits(LCD_CTRL_PORT, LCD_E_PIN)
#define LCD_E_L()      GPIO_ResetBits(LCD_CTRL_PORT, LCD_E_PIN)

// 函数声明
void lcd1602_gpio_init(void);
void lcd1602_write_cmd(uint8_t cmd);
void lcd1602_write_data(uint8_t dat);
void lcd1602_init(void);
void lcd1602_clear(void);
void lcd1602_show_string(uint8_t x, uint8_t y, uint8_t *str);
void delay_ms(uint32_t ms);

#endif





#include "lcd1602.h"

int main(void)
{
    lcd1602_init();
    lcd1602_show_string(0,0,(uint8_t*)"PB LCD1602 8bit");
    lcd1602_show_string(1,1,(uint8_t*)"StdLib No HAL");
    while(1);
}
相关推荐
小羊先生car18 小时前
软件:STM32-F1系列-读取Flash(2026/7/14)
stm32·单片机·嵌入式硬件
weixin_464078071 天前
Altium Designer自定义变压器
嵌入式硬件
QQ5286211241 天前
芯片程序提取的具体流程和方法
stm32·单片机·嵌入式硬件·程序·pcb抄板
LingzhiPi1 天前
零知派ESP32-S3-智能小车控制系统(4.2)-红外双目跟随模块使用
单片机·嵌入式硬件
智者知已应修善业1 天前
【proteus交通灯故障检测的完善方案】2025-4-29
经验分享·笔记·单片机·嵌入式硬件·proteus
LingzhiPi1 天前
零知ESP32--RC522NFC考勤打卡系统
c++·单片机·嵌入式硬件
炸膛坦客1 天前
常见代码问题与解答:(二)因外部晶振频率改变导致 MCU 锁死的原因及处理方法
单片机·嵌入式硬件
ShiMetaPi1 天前
ShiMetaPi Pico-G1:百元级黑光全彩AI ISP开发板
人工智能·嵌入式硬件·pico·isp
Lzh编程小栈1 天前
嵌入式深度精讲|STM32引脚全方位底层原理、配置逻辑、硬件规范、高频面试题
c语言·stm32·单片机·嵌入式硬件