news 2026/1/23 1:12:51

51单片机与LCD1602的通信代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
51单片机与LCD1602的通信代码

一 . 51 单片机与 LCD1602 的通信代码概述

LCD1602 是一种字符型液晶显示器,可显示 2 行 16 列字符,51 单片机与它的通信基于并行接口(8 位数据总线),通过控制引脚和时序配合实现数据传输。

1. 硬件连接(代码中定义的引脚)

  • 数据总线(8 位):通过P0 口与LCD的8pin数据总线连接,用于传输命令或数据.
  • 控制引脚(将单片机特定IO口定义为一下形式)
    • LCD_RS(寄存器选择):0 表示传输命令(如初始化、清屏),1 表示传输数据(如字符)。
    • LCD_RW(读写选择):0 表示写操作(单片机→LCD),1 表示读操作(LCD→单片机,代码中未使用,固定为 0)。
    • LCD_EN(使能信号):高电平有效,下降沿时 LCD 锁存总线上的数据 / 命令(核心控制信号)。
// 引脚配置(硬件连接定义) sbit LCD_RS = P2^6; // RS引脚接P2.6(寄存器选择:0=命令,1=数据) sbit LCD_RW = P2^5; // RW引脚接P2.5(读写选择:0=写,1=读) #define LCD_DataPort P0 // 数据口接P0(8位数据总线) sbit LCD_EN = P2^7; // EN引脚接P2.7(使能信号:高脉冲锁存数据)

2. 通信时序(写操作流程)

51 单片机向 LCD1602 写入数据 / 命令的核心流程如下(以写命令为例):

  1. 设置 RS 和 RWLCD_RS=0(传输命令)、LCD_RW=0(写)。
  2. 输出数据 / 命令:将 8 位命令通过LCD_DataPort(P0 口)输出到 LCD。
  3. 产生使能脉冲LCD_EN=1(LCD 准备接收)→ 延时(确保数据稳定)→LCD_EN=0(下降沿锁存数据,LCD 开始处理)。
  4. 等待处理完成:延时一段时间(LCD_Delay),确保 LCD 完成命令 / 数据的处理(避免连续操作导致错误)。
void LCD_WriteCommand(unsigned char Command) { LCD_RS = 0; // RS=0:表示传输命令 LCD_RW = 0; // RW=0:表示写操作 LCD_DataPort = Command; // 命令通过数据口输出 LCD_EN = 1; // EN=1:使能信号有效,LCD准备锁存数据 LCD_Delay(); // 延时等待数据稳定 LCD_EN = 0; // EN=0:下降沿锁存命令,完成写入 LCD_Delay(); // 延时等待LCD处理命令 }

写数据的流程与写命令完全相同,仅需将LCD_RS设为 1

3. 初始化流程

LCD1602 上电后需先初始化才能正常工作,代码中LCD_Init函数按以下步骤传输命令

  1. 0x38:功能设置→8 位数据接口、2 行显示、5×7 点阵字符(适配 LCD1602 硬件特性)。
  2. 0x0c:显示控制→开启显示、关闭光标、光标不闪烁(根据需求配置显示状态)。
  3. 0x06:输入方式→数据写入后光标自动右移,屏幕不滚动(确保后续字符连续显示)。
  4. 0x01:清屏→清除所有显示内容,光标复位到左上角(初始化完成后准备显示).
void LCD_Init() { LCD_WriteCommand(0x38); // 功能设置:8位数据接口,2行显示,5×7点阵字符 LCD_WriteCommand(0x0c); // 显示控制:显示开,光标关,光标不闪烁 LCD_WriteCommand(0x06); // 输入方式设置:数据写入后光标自动右移,屏幕不滚动 LCD_WriteCommand(0x01); // 清屏命令:清除所有显示内容,光标复位到原点 }

4.字符显示

LCD1602 内部有字符发生器 ROM(CGROM),存储了常用 ASCII 码字符的点阵数据。当单片机发送字符的 ASCII 码(如'A'的 0x41),LCD 会自动从 CGROM 中读取对应点阵并显示。

5.光标定位

通过函数LCD_SetCursor用于设置 LCD1602 液晶显示器的光标位置,以便从光标所在位置开始并往后写入字符或字符串或数据。下面结合 LCD1602 的硬件特性和代码逻辑详细解释:

1. 函数功能与参数

  • 功能:将 LCD1602 的光标定位到指定的行(Line)和列(Column),后续的写数据操作会从该位置开始显示。
  • 参数
    • Line:行位置,范围1~2(LCD1602 通常为 2 行显示)。
    • Column:列位置,范围1~16(LCD1602 每行可显示 16 个字符)。

2. 核心原理:LCD1602 的地址映射

LCD有2行16列一共32个方格,每个方格显示一个字符,每个方格都是一个5×7的点阵.

LCD1602 内部有一块DDRAM(数据显示存储器),每个方格在 DDRAM 中存在一个唯一地址。要在指定位置显示字符,需先通过命令将光标定位到特定方格。

  • DDRAM 地址规则:
    • 第 1 行所有方格的地址范围为0x00 ~ 0x0F(对应列 1~16)。
    • 第 2 行方格的地址范围为0x40 ~ 0x4F(对应列 1~16)。
  • 设置 DDRAM 地址的命令格式:0x80 | 地址值0x80是固定前缀,用于告知 LCD 这是一个 “设置 DDRAM 地址” 的命令,后续 7 位为某个方格的地址)。
void LCD_SetCursor(unsigned char Line, unsigned char Column) { if (Line == 1) { // 行1:设置光标到特定位置的命令格式: 0x80 | 地址值 //假设要将光标设置在第一行第一列的方格中,该方格的地址值为0x00,所以代码为(Column - 1) LCD_WriteCommand(0x80 | (Column - 1)); } else if (Line == 2) { // 行2: LCD_WriteCommand(0x80 | (Column - 1 + 0x40)); } }

6.数字 / 进制转换

数字(如 123)需先拆分为单个数字(1、2、3),转换为 ASCII 码('1'=0x31等)后,再按字符显示流程发送给 LCD。十六进制 / 二进制同理,需先拆分每一位并转换为对应字符(A~F 或 0~1)。

二 . LCD1602.c

#include <REGX52.H> // 51单片机寄存器定义头文件 // 引脚配置(硬件连接定义) sbit LCD_RS = P2^6; // RS引脚接P2.6(寄存器选择:0=命令,1=数据) sbit LCD_RW = P2^5; // RW引脚接P2.5(读写选择:0=写,1=读) #define LCD_DataPort P0 // 数据口接P0(8位数据总线) sbit LCD_EN = P2^7; // EN引脚接P2.7(使能信号:高脉冲锁存数据) // 内部函数定义(用户无需直接调用) /** * @brief LCD1602延时函数 * @param 无 * @retval 无 * @note 12MHz晶振下,该函数执行约1ms,用于满足LCD时序要求 */ void LCD_Delay() { unsigned char i, j; i = 2; j = 239; do { while (--j); // 内层循环:239次递减 } while (--i); // 外层循环:2次递减(总延时≈1ms) } /** * @brief 向LCD1602写入命令 * @param Command 要写入的命令(8位二进制,参考LCD1602 datasheet) * @retval 无 * @note 命令用于配置LCD(如初始化、设置光标、清屏等) */ void LCD_WriteCommand(unsigned char Command) { LCD_RS = 0; // RS=0:表示写入命令 LCD_RW = 0; // RW=0:表示写操作 LCD_DataPort = Command; // 命令通过数据口输出 LCD_EN = 1; // EN=1:使能信号有效,LCD准备锁存数据 LCD_Delay(); // 延时等待数据稳定 LCD_EN = 0; // EN=0:下降沿锁存命令,完成写入 LCD_Delay(); // 延时等待LCD处理命令 } /** * @brief 向LCD1602写入数据 * @param Data 要写入的数据(8位ASCII码,对应显示字符) * @retval 无 * @note 数据用于在LCD上显示(如字符、数字等) */ void LCD_WriteData(unsigned char Data) { LCD_RS = 1; // RS=1:表示写入数据 LCD_RW = 0; // RW=0:表示写操作 LCD_DataPort = Data; // 数据通过数据口输出 LCD_EN = 1; // EN=1:使能信号有效,LCD准备锁存数据 LCD_Delay(); // 延时等待数据稳定 LCD_EN = 0; // EN=0:下降沿锁存数据,完成写入 LCD_Delay(); // 延时等待LCD处理数据 } /** * @brief 设置LCD1602光标位置 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @retval 无 * @note LCD1602内部有地址映射,行1起始地址0x00,行2起始地址0x40 */ void LCD_SetCursor(unsigned char Line, unsigned char Column) { if (Line == 1) { // 行1:地址=0x80(命令前缀) + (列号-1)(列偏移) LCD_WriteCommand(0x80 | (Column - 1)); } else if (Line == 2) { // 行2:地址=0x80(命令前缀) + 0x40(行2偏移) + (列号-1)(列偏移) LCD_WriteCommand(0x80 | (Column - 1 + 0x40)); } } /** * @brief LCD1602初始化函数 * @param 无 * @retval 无 * @note 按LCD1602时序要求,依次发送初始化命令 */ void LCD_Init() { LCD_WriteCommand(0x38); // 功能设置:8位数据接口,2行显示,5×7点阵字符 LCD_WriteCommand(0x0c); // 显示控制:显示开,光标关,光标不闪烁 LCD_WriteCommand(0x06); // 输入方式设置:数据写入后光标自动右移,屏幕不滚动 LCD_WriteCommand(0x01); // 清屏命令:清除所有显示内容,光标复位到原点 } /** * @brief 在LCD1602指定位置显示单个字符(实现) * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符 * @retval 无 * @note 先设置光标位置,再写入字符数据 */ void LCD_ShowChar(unsigned char Line, unsigned char Column, char Char) { LCD_SetCursor(Line, Column); // 设置光标到目标位置 LCD_WriteData(Char); // 写入字符数据(ASCII码) } /** * @brief 在LCD1602指定位置显示字符串(实现) * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param String 要显示的字符串 * @retval 无 * @note 循环写入字符串中的每个字符,直到遇到'\0'结束符 */ void LCD_ShowString(unsigned char Line, unsigned char Column, char *String) { unsigned char i; LCD_SetCursor(Line, Column); // 设置起始光标位置 for (i = 0; String[i] != '\0'; i++) // 遍历字符串 { LCD_WriteData(String[i]); // 逐个写入字符 } } /** * @brief 内部辅助函数:计算X的Y次方(用于数字拆分) * @param X 底数(此处固定为10/16/2,对应十进制/十六进制/二进制) * @param Y 指数(数字位数) * @retval 计算结果(X^Y) */ int LCD_Pow(int X, int Y) { unsigned char i; int Result = 1; for (i = 0; i < Y; i++) { Result *= X; // 循环相乘Y次 } return Result; } /** * @brief 在LCD1602指定位置显示无符号数字(实现) * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字 * @param Length 显示位数 * @retval 无 * @note 按位拆分数字(如123拆分为1、2、3),转换为ASCII码后显示 */ void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length) { unsigned char i; LCD_SetCursor(Line, Column); // 设置起始位置 for (i = Length; i > 0; i--) // 从高位到低位处理 { // 取第i位数字(如i=3时,123 / 100 %10 = 1),加'0'转换为ASCII码 LCD_WriteData(Number / LCD_Pow(10, i - 1) % 10 + '0'); } } /** * @brief 在LCD1602指定位置显示有符号数字(实现) * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字 * @param Length 显示位数(含符号位) * @retval 无 * @note 先显示符号(+/-),再按无符号数处理绝对值 */ void LCD_ShowSignedNum(unsigned char Line, unsigned char Column, int Number, unsigned char Length) { unsigned char i; unsigned int Number1; // 用于存储绝对值(转为无符号处理) LCD_SetCursor(Line, Column); if (Number >= 0) { LCD_WriteData('+'); // 正数显示'+' Number1 = Number; } else { LCD_WriteData('-'); // 负数显示'-' Number1 = -Number; // 取绝对值 } // 后续处理与无符号数相同(Length已包含符号位,实际数字位数为Length-1) for (i = Length; i > 0; i--) { LCD_WriteData(Number1 / LCD_Pow(10, i - 1) % 10 + '0'); } } /** * @brief 在LCD1602指定位置显示十六进制数字(实现) * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字 * @param Length 显示位数 * @retval 无 * @note 0~9直接显示,10~15转换为A~F显示 */ void LCD_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length) { unsigned char i, SingleNumber; // SingleNumber存储每一位十六进制数 LCD_SetCursor(Line, Column); for (i = Length; i > 0; i--) { SingleNumber = Number / LCD_Pow(16, i - 1) % 16; // 取第i位十六进制数 if (SingleNumber < 10) { LCD_WriteData(SingleNumber + '0'); // 0~9:直接加'0'转ASCII } else { LCD_WriteData(SingleNumber - 10 + 'A'); // 10~15:转A~F } } } /** * @brief 在LCD1602指定位置显示二进制数字(实现) * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字 * @param Length 显示位数 * @retval 无 * @note 按位拆分二进制数,每一位为0或1,直接显示 */ void LCD_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length) { unsigned char i; LCD_SetCursor(Line, Column); for (i = Length; i > 0; i--) { // 取第i位二进制数(如i=3时,Number / 2^(i-1) %2),加'0'转ASCII LCD_WriteData(Number / LCD_Pow(2, i - 1) % 2 + '0'); } }

三 . LCD1602.h

#ifndef __LCD1602_H__ // 防止头文件重复包含 #define __LCD1602_H__ // 用户可调用的函数声明 /** * @brief LCD1602初始化函数 * @param 无 * @retval 无 * @note 初始化完成后,LCD1602进入可显示状态 */ void LCD_Init(); /** * @brief 在LCD1602指定位置显示单个字符 * @param Line 行位置,范围:1~2(LCD1602共2行) * @param Column 列位置,范围:1~16(每行16个字符) * @param Char 要显示的字符(ASCII码字符) * @retval 无 */ void LCD_ShowChar(unsigned char Line, unsigned char Column, char Char); /** * @brief 在LCD1602指定位置开始显示字符串 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param String 要显示的字符串(以'\0'结尾) * @retval 无 * @note 字符串长度超过剩余显示位置时,超出部分不显示 */ void LCD_ShowString(unsigned char Line, unsigned char Column, char *String); /** * @brief 在LCD1602指定位置开始显示无符号数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~65535(16位无符号数) * @param Length 要显示的数字位数,范围:1~5(不足补前导0) * @retval 无 */ void LCD_ShowNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length); /** * @brief 在LCD1602指定位置开始显示有符号数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:-32768~32767(16位有符号数) * @param Length 要显示的数字位数(含符号位),范围:1~5 * @retval 无 * @note 正数前显示'+',负数前显示'-',不足补前导0 */ void LCD_ShowSignedNum(unsigned char Line, unsigned char Column, int Number, unsigned char Length); /** * @brief 在LCD1602指定位置开始显示十六进制数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~0xFFFF(16位无符号数) * @param Length 要显示的位数,范围:1~4(不足补前导0) * @retval 无 * @note 字母A~F大写显示 */ void LCD_ShowHexNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length); /** * @brief 在LCD1602指定位置开始显示二进制数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~0xFFFF(16位无符号数) * @param Length 要显示的位数,范围:1~16(不足补前导0) * @retval 无 */ void LCD_ShowBinNum(unsigned char Line, unsigned char Column, unsigned int Number, unsigned char Length); #endif
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/22 14:31:33

基于Java实现优雅关闭的规范化方案设计与实现

文章目录前言背景初步调研实现思路方案核心需求分析技术方案对比实现思路初步功能设计关键设计决策实现步骤与代码第一步&#xff1a;核心关闭管理器实现第二步&#xff1a;集成到钉钉客户端管理器&#xff08;注册关闭&#xff09;总结说明资料获取前言 博主介绍&#xff1a;…

作者头像 李华
网站建设 2026/1/22 15:15:31

时序数据战场巅峰对决:金仓数据库 VS InfluxDB深度解析

引言 在物联网、工业互联网与运维监控领域&#xff0c;时序数据正以前所未有的速度迅猛增长。海量设备持续产生的数据流&#xff0c;对数据库提出了双重核心要求&#xff1a;既要支撑高速数据写入&#xff0c;又要实现快速复杂分析。长期以来&#xff0c;InfluxDB凭借时序领域的…

作者头像 李华
网站建设 2026/1/22 14:29:53

Windows任务管理器中CPU相关指标怎么看?

在Windows任务管理器中&#xff0c;CPU相关的指标是判断电脑性能和健康状况的核心。下面我为你详细拆解如何看懂它们&#xff0c;从最简单到最专业。 一、如何找到CPU指标&#xff1f; 打开任务管理器&#xff08;Ctrl Shift Esc&#xff09;。如果看到简化视图&#xff0…

作者头像 李华
网站建设 2026/1/22 14:35:03

【必藏】大模型入行晚了?现在就是黄金时机!小白到入门的完整路线

“现在学大模型还来得及吗&#xff1f;”最近在CSDN社区逛&#xff0c;总能刷到这类提问。我的答案永远是&#xff1a;种一棵树最好的时间是十年前&#xff0c;其次是现在——大模型领域尤其如此。 自ChatGPT引爆行业以来&#xff0c;AI大模型已经从“前沿概念”变成了企业刚需…

作者头像 李华
网站建设 2026/1/22 16:36:12

系统思考与认知习惯

我们常常低估一个事实&#xff1a;人并不是靠“思考能力”在行动&#xff0c;而是靠“认知惯性”在行动。绝大多数管理者在日常决策中&#xff0c;依赖的不是当下的理性分析&#xff0c;而是多年形成的自动反应系统&#xff0c;比如&#xff1a; 问题一出现&#xff0c;立刻找责…

作者头像 李华