想用过普中开发板来实现一个出租车计价器的方案,但是lcd1602无法正确显示,代码和开发板如下
单片机源程序如下:
- #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]);
- }
- }
复制代码 |