找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 62|回复: 3
打印 上一主题 下一主题
收起左侧

51单片机实验板lcd1602无法正确显示

[复制链接]
跳转到指定楼层
楼主
想用过普中开发板来实现一个出租车计价器的方案,但是lcd1602无法正确显示,代码和开发板如下


单片机源程序如下:
  1. #include <reg52.h>
  2. // #include <stdio.h> // 不再需要,可以移除

  3. // -------------------- 类型定义 --------------------
  4. typedef unsigned char u8;
  5. typedef unsigned int u16;

  6. // -------------------- 引脚定义 --------------------
  7. // LCD1602
  8. sbit LCD_RS = P2^6;
  9. sbit LCD_RW = P2^5;
  10. sbit LCD_EN = P2^7;
  11. #define LCD_DataPort P0

  12. // 独立按键
  13. sbit KEY_START_STOP = P3^1; // K1: 开始/停止
  14. sbit KEY_CLEAR = P3^0;      // K2: 清零 / 设置值-
  15. sbit KEY_MILEAGE = P3^2;    // K3: 里程+0.1 / 设置值+
  16. sbit KEY_SET = P3^3;        // K4: 设置

  17. // -------------------- 全局变量 --------------------
  18. // 计价器状态: 0-停止, 1-计价中, 2-设置模式
  19. u8 status = 0;
  20. // 设置模式子状态: 0-未设置, 1-设置起步价, 2-设置起步里程, 3-设置单价
  21. u8 setting_mode = 0;

  22. // --- 计价参数 (默认值) ---
  23. u16 base_price = 130;  // 起步价: 13.0 元
  24. u16 base_mileage = 30; // 起步里程: 3.0 公里
  25. u16 unit_price = 23;   // 单价: 2.3 元/公里

  26. // --- 实时数据 ---
  27. u16 mileage = 0;    // 行车里程 (x0.1 公里)
  28. u16 price = 0;      // 总价 (x0.1 元)
  29. u16 time_s = 0;     // 行车时长 (秒)
  30. u16 time_m = 0;     // 分
  31. u16 time_h = 0;     // 时

  32. // 定时器中断计数变量
  33. u16 timer_count = 0;

  34. // -------------------- 函数声明 --------------------
  35. // 延时
  36. void DelayMs(u16 ms);

  37. // LCD驱动
  38. void LcdWriteCmd(u8 cmd);
  39. void LcdWriteData(u8 dat);
  40. void LcdInit();
  41. void LcdSetCursor(u8 row, u8 col);
  42. void LcdShowString(u8 row, u8 col, u8 *str);
  43. void LcdShowNum(u8 row, u8 col, u16 num, u8 len); // 显示数字
  44. void LcdShowTime(); // 单独的时间显示函数

  45. // 显示界面
  46. void UpdateDisplay();
  47. void DisplaySettingMode();

  48. // 核心逻辑
  49. void CalculatePrice();
  50. void KeyAction();
  51. void ResetAll();
  52. void Timer0Init();


  53. // -------------------- 主函数 --------------------
  54. void main() {
  55.     Timer0Init();
  56.     LcdInit();
  57.     ResetAll();
  58.    
  59.     while(1) {
  60.         KeyAction();
  61.         if (status == 1) { // 只有在计时状态才需要不断刷新时间
  62.             LcdShowTime();
  63.         }
  64.     }
  65. }

  66. // -------------------- 核心逻辑函数 --------------------
  67. void CalculatePrice() {
  68.     if (mileage <= base_mileage) {
  69.         price = base_price;
  70.     } else {
  71.         unsigned long temp_price = (unsigned long)(mileage - base_mileage) * unit_price;
  72.         price = base_price + temp_price / 10;
  73.     }
  74.     // 价格计算后立即更新显示
  75.     if (status != 2) {
  76.         UpdateDisplay();
  77.     }
  78. }

  79. void KeyAction() {
  80.     if (KEY_START_STOP == 0) {
  81.         DelayMs(20);
  82.         if (KEY_START_STOP == 0) {
  83.             if (status != 2) {
  84.                 status = !status;
  85.                 if(status == 1 && mileage == 0){
  86.                     CalculatePrice(); // 开始时就计算一次价格
  87.                 } else if (status == 0) {
  88.                     // 停止计时,但显示保持不变
  89.                 }
  90.                  UpdateDisplay(); // 更新显示(特别是状态字符)
  91.             }
  92.         }
  93.         while(KEY_START_STOP == 0);
  94.     }
  95.    
  96.     if (KEY_CLEAR == 0) {
  97.         DelayMs(20);
  98.         if (KEY_CLEAR == 0) {
  99.             if (status == 0) {
  100.                 ResetAll();
  101.             } else if (status == 2) {
  102.                 switch(setting_mode) {
  103.                     case 1: if(base_price > 50) base_price -= 10; break;
  104.                     case 2: if(base_mileage > 10) base_mileage -= 10; break;
  105.                     case 3: if(unit_price > 10) unit_price -= 1; break;
  106.                 }
  107.                 DisplaySettingMode();
  108.             }
  109.         }
  110.         while(KEY_CLEAR == 0);
  111.     }
  112.    
  113.     if (KEY_MILEAGE == 0) {
  114.         DelayMs(20);
  115.         if (KEY_MILEAGE == 0) {
  116.             if (status == 1) {
  117.                 mileage++;
  118.                 CalculatePrice();
  119.             } else if (status == 2) {
  120.                 switch(setting_mode) {
  121.                     case 1: base_price += 10; break;
  122.                     case 2: base_mileage += 10; break;
  123.                     case 3: unit_price += 1; break;
  124.                 }
  125.                 DisplaySettingMode();
  126.             }
  127.         }
  128.         while(KEY_MILEAGE == 0);
  129.     }
  130.    
  131.     if (KEY_SET == 0) {
  132.         DelayMs(20);
  133.         if (KEY_SET == 0) {
  134.             if(status == 1) return;

  135.             status = 2;
  136.             setting_mode++;
  137.             if (setting_mode > 3) {
  138.                 setting_mode = 0;
  139.                 status = 0;
  140.                 ResetAll();
  141.             } else {
  142.                 DisplaySettingMode();
  143.             }
  144.         }
  145.         while(KEY_SET == 0);
  146.     }
  147. }

  148. void ResetAll() {
  149.     status = 0; // 确保是停止状态
  150.     mileage = 0;
  151.     price = 0;
  152.     time_s = 0;
  153.     time_m = 0;
  154.     time_h = 0;
  155.     T0 = 0; // 如果有,也停止计时器
  156.     UpdateDisplay(); // 更新整个显示
  157.     LcdShowTime();
  158. }

  159. // -------------------- 显示相关函数 (无sprintf) --------------------
  160. // 更新主显示界面(里程和价格)
  161. void UpdateDisplay() {
  162.     // 第一行: L:XXX.XKM P:XXXX.X
  163.     LcdShowString(0, 0, "L:    KM P:     "); // 模板,用空格覆盖旧数据
  164.     LcdShowNum(0, 2, mileage / 10, 3); // 里程整数部分,最多3位
  165.     LcdWriteData('.');
  166.     LcdShowNum(0, 6, mileage % 10, 1); // 里程小数部分
  167.    
  168.     LcdShowNum(0, 11, price / 10, 4); // 价格整数部分,最多4位
  169.     LcdWriteData('.');
  170.     LcdShowNum(0, 16, price % 10, 1); // 价格小数部分

  171.     // 在时间前面显示状态符号
  172.     LcdSetCursor(1, 0);
  173.     if(status == 1) LcdWriteData('T'); // Timing
  174.     else if(status == 0) LcdWriteData('S'); // Stop
  175.     else if(status == 2) LcdWriteData('C'); // Config
  176. }

  177. // 单独更新时间显示,减少LCD刷新
  178. void LcdShowTime() {
  179.     // 第二行: T:HH:MM:SS
  180.     LcdSetCursor(1, 1);
  181.     LcdShowString(1, 1, ":");
  182.     LcdShowNum(1, 2, time_h, 2);
  183.     LcdShowString(1, 4, ":");
  184.     LcdShowNum(1, 5, time_m, 2);
  185.     LcdShowString(1, 7, ":");
  186.     LcdShowNum(1, 8, time_s, 2);
  187. }

  188. // 显示设置模式界面
  189. void DisplaySettingMode() {
  190.     LcdWriteCmd(0x01); // 清屏
  191.     UpdateDisplay();   // 显示状态'C'
  192.     switch(setting_mode) {
  193.         case 1:
  194.             LcdShowString(0, 0, "Set Base Price  ");
  195.             LcdShowNum(1, 5, base_price/10, 3);
  196.             LcdWriteData('.');
  197.             LcdShowNum(1, 9, base_price%10, 1);
  198.             LcdShowString(1, 10, " Yuan ");
  199.             break;
  200.         case 2:
  201.             LcdShowString(0, 0, "Set Base Mile   ");
  202.             LcdShowNum(1, 5, base_mileage/10, 2);
  203.             LcdWriteData('.');
  204.             LcdShowNum(1, 8, base_mileage%10, 1);
  205.             LcdShowString(1, 9, " KM   ");
  206.             break;
  207.         case 3:
  208.             LcdShowString(0, 0, "Set Unit Price  ");
  209.             LcdShowNum(1, 5, unit_price/10, 2);
  210.             LcdWriteData('.');
  211.             LcdShowNum(1, 8, unit_price%10, 1);
  212.             LcdShowString(1, 9, " Y/KM ");
  213.             break;
  214.     }
  215. }

  216. // -------------------- 定时器和中断 --------------------
  217. void Timer0Init() {
  218.     TMOD &= 0xF0;
  219.     TMOD |= 0x01;
  220.     TH0 = (65536 - 1000) / 256;
  221.     TL0 = (65536 - 1000) % 256;
  222.     EA = 1;
  223.     ET0 = 1;
  224.     TR0 = 1;
  225. }

  226. void timer0_isr() interrupt 1 {
  227.     TH0 = (65536 - 1000) / 256;
  228.     TL0 = (65536 - 1000) % 256;

  229.     if (status == 1) {
  230.         timer_count++;
  231.         if (timer_count >= 1000) {
  232.             timer_count = 0;
  233.             time_s++;
  234.             if (time_s >= 60) {
  235.                 time_s = 0;
  236.                 time_m++;
  237.                 if (time_m >= 60) {
  238.                     time_m = 0;
  239.                     time_h++;
  240.                 }
  241.             }
  242.         }
  243.     }
  244. }

  245. // -------------------- LCD底层驱动 --------------------
  246. void DelayMs(u16 ms) {
  247.     u16 i, j;
  248.     for(i=ms; i>0; i--)
  249.         for(j=120; j>0; j--);
  250. }

  251. void LcdWriteCmd(u8 cmd) {
  252.     LCD_RS = 0;
  253.     LCD_RW = 0;
  254.     LCD_DataPort = cmd;
  255.     DelayMs(1);
  256.     LCD_EN = 1;
  257.     DelayMs(1);
  258.     LCD_EN = 0;
  259. }

  260. void LcdWriteData(u8 dat) {
  261.     LCD_RS = 1;
  262.     LCD_RW = 0;
  263.     LCD_DataPort = dat;
  264.     DelayMs(1);
  265.     LCD_EN = 1;
  266.     DelayMs(1);
  267.     LCD_EN = 0;
  268. }

  269. void LcdInit() {
  270.     LcdWriteCmd(0x38);
  271.     LcdWriteCmd(0x0C);
  272.     LcdWriteCmd(0x06);
  273.     LcdWriteCmd(0x01);
  274.     DelayMs(5);
  275. }

  276. void LcdSetCursor(u8 row, u8 col) {
  277.     u8 addr;
  278.     if (row == 0) {
  279.         addr = 0x80 + col;
  280.     } else {
  281.         addr = 0x80 + 0x40 + col;
  282.     }
  283.     LcdWriteCmd(addr);
  284. }

  285. void LcdShowString(u8 row, u8 col, u8 *str) {
  286.     LcdSetCursor(row, col);
  287.     while(*str != '\0') {
  288.         LcdWriteData(*str++);
  289.     }
  290. }

  291. // *** 新增的显示数字的函数 ***
  292. // 在(row, col)显示数字num,共显示len位,不足则前面补空格
  293. void LcdShowNum(u8 row, u8 col, u16 num, u8 len) {
  294.     u8 i;
  295.     u8 buf[5]; // 假设数字最大5位
  296.     for (i = 0; i < len; i++) {
  297.         buf[len - 1 - i] = (num % 10) + '0';
  298.         num /= 10;
  299.     }
  300.    
  301.     // 如果数字本身位数小于len,前面补空格
  302.     for (i = 0; i < len; i++) {
  303.         if (num == 0 && buf[i] == '0' && i < len - 1) { // 找到最高位
  304.              if(i < len - 1) buf[i] = ' '; // 将高位0转换为空格, 但保留个位数0
  305.         } else {
  306.             break; // 找到第一个非零数字后停止
  307.         }
  308.     }
  309.     // 处理num=0的情况
  310.     if (len > 1 && buf[0] == ' ') {
  311.          // 确保个位数如果是0,能正常显示
  312.          if(len > 1 && num == 0){
  313.              // 此处确保至少有一个'0'显示
  314.          }
  315.     }


  316.     LcdSetCursor(row, col);
  317.     for (i = 0; i < len; i++) {
  318.         LcdWriteData(buf[i]);
  319.     }
  320. }
复制代码
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:276663 发表于 2025-7-9 13:39 | 只看该作者
1,检查pin脚定义,
2,检查端口是否有输出正确的数据,
3,检查数据的LSB还是MSB.
回复

使用道具 举报

板凳
ID:584814 发表于 2025-7-9 15:46 | 只看该作者
学习板自带的1602例程移植就好。
回复

使用道具 举报

地板
ID:1152291 发表于 2025-7-9 16:28 | 只看该作者
首先你使用LCD1602的液晶屏应该是有自带的驱动程序。从你需要实现的目的开始求我们现在,你的要是没有看到的。不知道你的具体要求是什莫。你是不是觉得LCD1602比较简单,我给你说我之前用普中科技的板子写代码用来写字符的时候时,我也出错了。所以我大概率判断是你的LCD1602的驱动程序有问题,是需要去查看LCD1602驱动程序的问题。也有可能是你的端口输出数据的问题,数据到底是LSB还是MSB呢,你是不是把数据的类型搞错了哦。是不是输出的数据类型不对等的呢。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表