标题:
51单片机实验板lcd1602无法正确显示
[打印本页]
作者:
Acrux
时间:
2025-7-9 11:18
标题:
51单片机实验板lcd1602无法正确显示
想用过普中开发板来实现一个出租车计价器的方案,但是lcd1602无法正确显示,代码和开发板如下
1.jpg
(137.84 KB, 下载次数: 0)
下载附件
2025-7-9 12:10 上传
单片机源程序如下:
#include <reg52.h>
// #include <stdio.h> // 不再需要,可以移除
// -------------------- 类型定义 --------------------
typedef unsigned char u8;
typedef unsigned int u16;
// -------------------- 引脚定义 --------------------
// LCD1602
sbit LCD_RS = P2^6;
sbit LCD_RW = P2^5;
sbit LCD_EN = P2^7;
#define LCD_DataPort P0
// 独立按键
sbit KEY_START_STOP = P3^1; // K1: 开始/停止
sbit KEY_CLEAR = P3^0; // K2: 清零 / 设置值-
sbit KEY_MILEAGE = P3^2; // K3: 里程+0.1 / 设置值+
sbit KEY_SET = P3^3; // K4: 设置
// -------------------- 全局变量 --------------------
// 计价器状态: 0-停止, 1-计价中, 2-设置模式
u8 status = 0;
// 设置模式子状态: 0-未设置, 1-设置起步价, 2-设置起步里程, 3-设置单价
u8 setting_mode = 0;
// --- 计价参数 (默认值) ---
u16 base_price = 130; // 起步价: 13.0 元
u16 base_mileage = 30; // 起步里程: 3.0 公里
u16 unit_price = 23; // 单价: 2.3 元/公里
// --- 实时数据 ---
u16 mileage = 0; // 行车里程 (x0.1 公里)
u16 price = 0; // 总价 (x0.1 元)
u16 time_s = 0; // 行车时长 (秒)
u16 time_m = 0; // 分
u16 time_h = 0; // 时
// 定时器中断计数变量
u16 timer_count = 0;
// -------------------- 函数声明 --------------------
// 延时
void DelayMs(u16 ms);
// LCD驱动
void LcdWriteCmd(u8 cmd);
void LcdWriteData(u8 dat);
void LcdInit();
void LcdSetCursor(u8 row, u8 col);
void LcdShowString(u8 row, u8 col, u8 *str);
void LcdShowNum(u8 row, u8 col, u16 num, u8 len); // 显示数字
void LcdShowTime(); // 单独的时间显示函数
// 显示界面
void UpdateDisplay();
void DisplaySettingMode();
// 核心逻辑
void CalculatePrice();
void KeyAction();
void ResetAll();
void Timer0Init();
// -------------------- 主函数 --------------------
void main() {
Timer0Init();
LcdInit();
ResetAll();
while(1) {
KeyAction();
if (status == 1) { // 只有在计时状态才需要不断刷新时间
LcdShowTime();
}
}
}
// -------------------- 核心逻辑函数 --------------------
void CalculatePrice() {
if (mileage <= base_mileage) {
price = base_price;
} else {
unsigned long temp_price = (unsigned long)(mileage - base_mileage) * unit_price;
price = base_price + temp_price / 10;
}
// 价格计算后立即更新显示
if (status != 2) {
UpdateDisplay();
}
}
void KeyAction() {
if (KEY_START_STOP == 0) {
DelayMs(20);
if (KEY_START_STOP == 0) {
if (status != 2) {
status = !status;
if(status == 1 && mileage == 0){
CalculatePrice(); // 开始时就计算一次价格
} else if (status == 0) {
// 停止计时,但显示保持不变
}
UpdateDisplay(); // 更新显示(特别是状态字符)
}
}
while(KEY_START_STOP == 0);
}
if (KEY_CLEAR == 0) {
DelayMs(20);
if (KEY_CLEAR == 0) {
if (status == 0) {
ResetAll();
} else if (status == 2) {
switch(setting_mode) {
case 1: if(base_price > 50) base_price -= 10; break;
case 2: if(base_mileage > 10) base_mileage -= 10; break;
case 3: if(unit_price > 10) unit_price -= 1; break;
}
DisplaySettingMode();
}
}
while(KEY_CLEAR == 0);
}
if (KEY_MILEAGE == 0) {
DelayMs(20);
if (KEY_MILEAGE == 0) {
if (status == 1) {
mileage++;
CalculatePrice();
} else if (status == 2) {
switch(setting_mode) {
case 1: base_price += 10; break;
case 2: base_mileage += 10; break;
case 3: unit_price += 1; break;
}
DisplaySettingMode();
}
}
while(KEY_MILEAGE == 0);
}
if (KEY_SET == 0) {
DelayMs(20);
if (KEY_SET == 0) {
if(status == 1) return;
status = 2;
setting_mode++;
if (setting_mode > 3) {
setting_mode = 0;
status = 0;
ResetAll();
} else {
DisplaySettingMode();
}
}
while(KEY_SET == 0);
}
}
void ResetAll() {
status = 0; // 确保是停止状态
mileage = 0;
price = 0;
time_s = 0;
time_m = 0;
time_h = 0;
T0 = 0; // 如果有,也停止计时器
UpdateDisplay(); // 更新整个显示
LcdShowTime();
}
// -------------------- 显示相关函数 (无sprintf) --------------------
// 更新主显示界面(里程和价格)
void UpdateDisplay() {
// 第一行: L:XXX.XKM P:XXXX.X
LcdShowString(0, 0, "L: KM P: "); // 模板,用空格覆盖旧数据
LcdShowNum(0, 2, mileage / 10, 3); // 里程整数部分,最多3位
LcdWriteData('.');
LcdShowNum(0, 6, mileage % 10, 1); // 里程小数部分
LcdShowNum(0, 11, price / 10, 4); // 价格整数部分,最多4位
LcdWriteData('.');
LcdShowNum(0, 16, price % 10, 1); // 价格小数部分
// 在时间前面显示状态符号
LcdSetCursor(1, 0);
if(status == 1) LcdWriteData('T'); // Timing
else if(status == 0) LcdWriteData('S'); // Stop
else if(status == 2) LcdWriteData('C'); // Config
}
// 单独更新时间显示,减少LCD刷新
void LcdShowTime() {
// 第二行: T:HH:MM:SS
LcdSetCursor(1, 1);
LcdShowString(1, 1, ":");
LcdShowNum(1, 2, time_h, 2);
LcdShowString(1, 4, ":");
LcdShowNum(1, 5, time_m, 2);
LcdShowString(1, 7, ":");
LcdShowNum(1, 8, time_s, 2);
}
// 显示设置模式界面
void DisplaySettingMode() {
LcdWriteCmd(0x01); // 清屏
UpdateDisplay(); // 显示状态'C'
switch(setting_mode) {
case 1:
LcdShowString(0, 0, "Set Base Price ");
LcdShowNum(1, 5, base_price/10, 3);
LcdWriteData('.');
LcdShowNum(1, 9, base_price%10, 1);
LcdShowString(1, 10, " Yuan ");
break;
case 2:
LcdShowString(0, 0, "Set Base Mile ");
LcdShowNum(1, 5, base_mileage/10, 2);
LcdWriteData('.');
LcdShowNum(1, 8, base_mileage%10, 1);
LcdShowString(1, 9, " KM ");
break;
case 3:
LcdShowString(0, 0, "Set Unit Price ");
LcdShowNum(1, 5, unit_price/10, 2);
LcdWriteData('.');
LcdShowNum(1, 8, unit_price%10, 1);
LcdShowString(1, 9, " Y/KM ");
break;
}
}
// -------------------- 定时器和中断 --------------------
void Timer0Init() {
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = (65536 - 1000) / 256;
TL0 = (65536 - 1000) % 256;
EA = 1;
ET0 = 1;
TR0 = 1;
}
void timer0_isr() interrupt 1 {
TH0 = (65536 - 1000) / 256;
TL0 = (65536 - 1000) % 256;
if (status == 1) {
timer_count++;
if (timer_count >= 1000) {
timer_count = 0;
time_s++;
if (time_s >= 60) {
time_s = 0;
time_m++;
if (time_m >= 60) {
time_m = 0;
time_h++;
}
}
}
}
}
// -------------------- LCD底层驱动 --------------------
void DelayMs(u16 ms) {
u16 i, j;
for(i=ms; i>0; i--)
for(j=120; j>0; j--);
}
void LcdWriteCmd(u8 cmd) {
LCD_RS = 0;
LCD_RW = 0;
LCD_DataPort = cmd;
DelayMs(1);
LCD_EN = 1;
DelayMs(1);
LCD_EN = 0;
}
void LcdWriteData(u8 dat) {
LCD_RS = 1;
LCD_RW = 0;
LCD_DataPort = dat;
DelayMs(1);
LCD_EN = 1;
DelayMs(1);
LCD_EN = 0;
}
void LcdInit() {
LcdWriteCmd(0x38);
LcdWriteCmd(0x0C);
LcdWriteCmd(0x06);
LcdWriteCmd(0x01);
DelayMs(5);
}
void LcdSetCursor(u8 row, u8 col) {
u8 addr;
if (row == 0) {
addr = 0x80 + col;
} else {
addr = 0x80 + 0x40 + col;
}
LcdWriteCmd(addr);
}
void LcdShowString(u8 row, u8 col, u8 *str) {
LcdSetCursor(row, col);
while(*str != '\0') {
LcdWriteData(*str++);
}
}
// *** 新增的显示数字的函数 ***
// 在(row, col)显示数字num,共显示len位,不足则前面补空格
void LcdShowNum(u8 row, u8 col, u16 num, u8 len) {
u8 i;
u8 buf[5]; // 假设数字最大5位
for (i = 0; i < len; i++) {
buf[len - 1 - i] = (num % 10) + '0';
num /= 10;
}
// 如果数字本身位数小于len,前面补空格
for (i = 0; i < len; i++) {
if (num == 0 && buf[i] == '0' && i < len - 1) { // 找到最高位
if(i < len - 1) buf[i] = ' '; // 将高位0转换为空格, 但保留个位数0
} else {
break; // 找到第一个非零数字后停止
}
}
// 处理num=0的情况
if (len > 1 && buf[0] == ' ') {
// 确保个位数如果是0,能正常显示
if(len > 1 && num == 0){
// 此处确保至少有一个'0'显示
}
}
LcdSetCursor(row, col);
for (i = 0; i < len; i++) {
LcdWriteData(buf[i]);
}
}
复制代码
作者:
IdeaMing
时间:
2025-7-9 13:39
1,检查pin脚定义,
2,检查端口是否有输出正确的数据,
3,检查数据的LSB还是MSB.
作者:
man1234567
时间:
2025-7-9 15:46
学习板自带的1602例程移植就好。
作者:
单片机重购
时间:
2025-7-9 16:28
首先你使用LCD1602的液晶屏应该是有自带的驱动程序。从你需要实现的目的开始求我们现在,你的要是没有看到的。不知道你的具体要求是什莫。你是不是觉得LCD1602比较简单,我给你说我之前用普中科技的板子写代码用来写字符的时候时,我也出错了。所以我大概率判断是你的LCD1602的驱动程序有问题,是需要去查看LCD1602驱动程序的问题。也有可能是你的端口输出数据的问题,数据到底是LSB还是MSB呢,你是不是把数据的类型搞错了哦。是不是输出的数据类型不对等的呢。
作者:
hhdsdy
时间:
2025-7-9 18:07
这个程序是从这块开发板的1602移植的吗?看照片各个模块的引脚是固定死无法自己定义的,所以软件得服从硬件,先把各个引脚定义为硬件的实际连接才行。
作者:
转角遇见劫匪
时间:
2025-7-9 18:30
首先你使用LCD1602的液晶屏应该是有自带的驱动程序。从你需要实现的目的开始求我们现在,你的要是没有看到的。不知道你的具体要求是什莫。你是不是觉得LCD1602比较简单,我给你说我之前用普中科技的板子写代码用来写字符的时候时,我也出错了。所以我大概率判断是你的LCD1602的驱动程序有问题,是需要去查看LCD1602驱动程序的问题。也有可能是你的端口输出数据的问题,数据到底是LSB还是MSB呢.
作者:
hhdsdy
时间:
2025-7-9 22:47
地板楼的“单片机重购”和6楼的“ID:1156151”帖子怎么一模一样?是同一个人吗?
作者:
人中狼
时间:
2025-7-10 16:43
没查忙
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1