找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 763|回复: 13
收起左侧

求助单片机菜单程序

  [复制链接]
ID:579174 发表于 2024-7-25 21:23 | 显示全部楼层 |阅读模式
200黑币
51单片机初学者,想用4个按键实现菜单设置,K1为菜单翻页,K2/K3为数值的加减,K4为保存退出,2个数码管显示,一个数码管显示菜单页面如P01,另一个数码管显示菜单对应的值,如数字123或ON和OFF。困惑的是  进入对应页面如何获取菜单对应的初值,求大神指导!

回复

使用道具 举报

ID:404160 发表于 2024-7-26 11:20 | 显示全部楼层
编写一个使用C51单片机的程序,实现按键控制菜单设置的功能,需要考虑以下几个方面:

1. 按键的初始化和防抖动处理。
2. 数码管的驱动和显示。
3. 菜单逻辑的设计,包括翻页、数值加减和保存退出。
4. 程序结构的安排,确保代码的可读性和可维护性。

下面是一个简单的示例代码,用于实现上述功能。请注意,这只是一个基础示例,实际应用中可能需要根据具体的硬件连接和需求进行调整。

```c
#include <reg51.h>

#define KEY1 P3_0
#define KEY2 P3_1
#define KEY3 P3_2
#define KEY4 P3_3

#define SEGMENT_PORT P0 // 数码管的段选端口
#define DIGIT_PORT P1  // 数码管的位选端口

// 假设菜单有3个页面,每个页面有2个可设置的值
#define MAX_PAGE 3
#define MAX_VALUE 2

unsigned char currentPage = 0;
unsigned char currentValIndex = 0;
unsigned int values[MAX_PAGE][MAX_VALUE] = {
    {100, 200}, // 第一个页面的两个值
    {300, 400}, // 第二个页面的两个值
    {500, 600}  // 第三个页面的两个值
};

// 延时函数
void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 123; j++);
}

// 按键扫描函数
unsigned char keyScan() {
    if (KEY1 == 0) {
        delay(20); // 简单的消抖
        if (KEY1 == 0) {
            currentPage = (currentPage + 1) % MAX_PAGE; // 翻页
            return 1;
        }
    }
    if (KEY2 == 0) {
        delay(20);
        if (KEY2 == 0) {
            values[currentPage][currentValIndex]++; // 增加数值
            return 2;
        }
    }
    if (KEY3 == 0) {
        delay(20);
        if (KEY3 == 0) {
            values[currentPage][currentValIndex]--; // 减少数值
            return 3;
        }
    }
    if (KEY4 == 0) {
        delay(20);
        if (KEY4 == 0) {
            return 4; // 保存退出
        }
    }
    return 0;
}

// 数码管显示函数
void display(unsigned char page, unsigned int value) {
    // 这里需要根据实际的数码管硬件连接来编写显示逻辑
    // 以下代码仅为示例
    SEGMENT_PORT = 0xFF; // 关闭所有段
    DIGIT_PORT = ~(1 << page); // 选择当前显示的数码管

    // 将数值转换为数码管可以显示的格式
    // 这里需要编写转换逻辑,例如将数值转换为BCD码等
    unsigned char displayValue = value % 10; // 仅显示个位数作为示例

    SEGMENT_PORT = ~displayValue; // 打开相应的段
}

void main() {
    while (1) {
        unsigned char key = keyScan();
        if (key == 1) {
            // 翻页
            display(0, currentPage + 1); // 显示页面号
        } else if (key == 2 || key == 3) {
            // 数值加减
            display(1, values[currentPage][currentValIndex]); // 显示当前值
        } else if (key == 4) {
            // 保存退出
            // 这里可以添加保存逻辑,例如写入EEPROM等
            break;
        }
    }
}
```

请注意,这个示例代码没有包含所有的硬件细节,例如数码管的驱动逻辑和按键的防抖动处理。在实际应用中,你可能需要根据具体的硬件连接和需求来编写相应的驱动代码。此外,对于数值的显示,你可能需要将数值转换为BCD码或其他格式,以便数码管可以正确显示。
回复

使用道具 举报

ID:548322 发表于 2024-7-26 15:00 | 显示全部楼层
楼上用chatgpt回复也挺⑥的,不过这边建议作为51单片机初学者,可以先尝试两个界面如何显示不同数据,再做菜单界面,一般STM32的菜单界面都是通过链表的方式实现,可以用stc试试枚举+结构体的方式去做。如果等人给你一个开源工程,基本上很难。
回复

使用道具 举报

ID:1116913 发表于 2024-7-26 15:31 | 显示全部楼层
回复

使用道具 举报

ID:579174 发表于 2024-7-26 19:48 | 显示全部楼层
2300606886 发表于 2024-7-26 15:31
http://www.51hei.com/bbs/dpj-232305-1.html可以看看他的程序

感谢!
回复

使用道具 举报

ID:579174 发表于 2024-7-26 19:48 | 显示全部楼层
小盼666 发表于 2024-7-26 15:00
楼上用chatgpt回复也挺⑥的,不过这边建议作为51单片机初学者,可以先尝试两个界面如何显示不同数据,再做 ...

非常感谢!
回复

使用道具 举报

ID:1129675 发表于 2024-7-27 00:07 | 显示全部楼层
可以用状态机做
回复

使用道具 举报

ID:982617 发表于 2024-8-7 17:13 | 显示全部楼层
要实现这个功能,你需要构建一个状态机以管理不同菜单页面的控制流程。下面是一个基于51单片机的简单实现思路:

### 步骤分解:

#### 1. 定义菜单页面和状态:

- **菜单页面**:假设你有3个主要菜单页面,可以是:
  - 页面1:初值为0的数值设置。
  - 页面2:初值为ON或OFF的状态设置。
  - 页面3:初值为123的数值设置。
  
- **状态**:需要的状态包括但不限于:
  - 状态1:显示页面1,并显示对应的数值或状态。
  - 状态2:显示页面2,并显示ON或OFF状态。
  - 状态3:显示页面3,并显示数值123。

#### 2. 初始化:

- 设置数码管显示模式,如使用动态扫描显示。
- 初始化按键输入,如使用中断或循环检查按键状态。
- 将所有菜单页的初始值存储在存储器中,例如在特殊功能寄存器(SFR)或程序存储器中。

#### 3. 状态机设计:

- **状态机**使用状态寄存器来跟踪当前的菜单页面和状态。
- **状态转移**通过按键输入来触发,例如,K1按键用于翻页,K2/K3按键用于数值加减,K4按键用于保存设置并退出当前页面或保存设置后返回上一页。

#### 4. 编写程序:

以下是一个基于简单假设的伪代码示例:

```c
#include <reg51.h> // 51单片机头文件

#define LCD_DATA_PORT P0 // LCD数据端口
#define LCD_CMD_PORT P2 // LCD命令端口
#define SENSOR_PORT P1 // 按键端口
#define PAGE1_INIT_VALUE 0 // 初始值为0
#define PAGE2_INIT_VALUE "ON" // 初始状态为ON
#define PAGE3_INIT_VALUE 123 // 初始值为123
#define PAGE_COUNT 3 // 3个菜单页面

void setup() {
    // 初始化数码管,按键,和LCD
}

unsigned char currentState = 1; // 菜单页面状态
unsigned char currentValue = PAGE1_INIT_VALUE; // 当前菜单的值
unsigned char currentDisplay = PAGE1_INIT_VALUE; // 当前显示的值
char displayValue[2]; // 用于显示的数值字符串

void loop() {
    // 检查按键状态并根据状态机更新
    if (K1) {
        // 翻页操作
    } else if (K2) {
        // 数值加操作
    } else if (K3) {
        // 数值减操作
    } else if (K4) {
        // 保存退出操作
    }

    // 显示操作
    displayCurrentPage();
   
    // 根据状态更改显示
    if (currentState == 1) {
        // 显示页面1
    } else if (currentState == 2) {
        // 显示页面2
    } else if (currentState == 3) {
        // 显示页面3
    }
}

void displayCurrentPage() {
    // 显示当前值或状态到数码管
}

// 更多细节和逻辑填充...
```

### 实现细节:

- **按键输入**:使用中断或者循环检查按键状态。
- **数码管显示**:使用动态扫描或静态显示方法更新数码管显示。
- **状态机逻辑**:根据按键输入更新状态寄存器,从而控制数码管显示的内容和执行相应的操作。

这个框架提供了基本的思路和结构,你需要根据实际的硬件和软件环境进一步完善代码。特别是按键的处理和数码管显示的具体实现,可能需要参考具体硬件的驱动库或参考资料。
回复

使用道具 举报

ID:579174 发表于 2024-8-12 10:04 | 显示全部楼层
hezezeze 发表于 2024-8-7 17:13
要实现这个功能,你需要构建一个状态机以管理不同菜单页面的控制流程。下面是一个基于51单片机的简单实现思 ...

受教!
回复

使用道具 举报

ID:123917 发表于 2024-8-12 19:16 | 显示全部楼层
思路,R0的值对应菜单号码,比如R0=1,显示P01,R0=2,显示P02
回复

使用道具 举报

ID:309610 发表于 2024-8-18 09:46 | 显示全部楼层
先做一个按键扫描函数返回值 0,无按键,1菜单按键、2上翻、3下翻、4确定保存、5超时60s无按键.具体程序很多可以参考其他
做一个显示函数,disp_menu(u8 menu)显示菜单,disp_datp(u8 data)显示菜单内容
如果菜单参数是同类型,定义菜单数组就行。不同类型要定义结构体。
#define XX  10      //XX菜单数目
u8 menu=0; //菜单号   0为运行显示内容,非0菜单调整
u8 menu_last=0; //历史菜单号
u8 datp_temp=0;// 菜单临时寄存
u8 datp_menu[XX];//菜单参数表

void main()
{
  while(1)
  {
    u8 key=keyscan();//读取按键
       
    if(key==1)                      //菜单键 循环菜单
          { menu++; if(menu>XX)menu=0;}
        else if(key==2)                //上翻 临时参数加
          { datp_temp++;}         
    else if(key==3)                //下翻 临时参数减
          { datp_temp--;}                
        else if(key==4)                //保存 临时参数保存
                { if(menu>0)
                     {datp_menu[menu-1]=datp_temp;}         
                                 }
        else if(key==5)                 //超时退出菜单
                {menu=0;}         

        if ((menu!=menu_last)&&(menu>0)) //菜单改变读取菜单保存到临时寄存
            { datp_temp=datp_menu[menu-1]
        menu_last=menu;                   //菜单号寄存
               
         if (menu==0) { disp();}          //正常显示程序
         else {  disp_menu( menu);       //显示菜单号
                 disp_datp(datp_temp);    // 显示菜单内容
                         }
        }
}
  此程序适合参数较少,参数不大的简易菜单或调试学习。进一步上翻键改为移位键增加一转换函数把指定参数
  整形拆分为个、 十、百、千、万 、小数。存储到临时数组内,移位键修改指针地址。兼修改位的闪烁显示标志、
  下翻修改制定临时数组内容。这样可以设定多种类型参数,可以设定较大数值。按保存键再把临时数组内内容整合
  保存到所读参数内。
回复

使用道具 举报

ID:1125252 发表于 2024-8-20 18:44 | 显示全部楼层
if(K1==0)
{
  num++;
  if(num>=4)
  {

  }
}
回复

使用道具 举报

ID:1125252 发表于 2024-8-20 18:52 | 显示全部楼层
if(k1==0)
{
num++;
if(num>=4){num=0;}
}
if(num==0){读取eeprom的addr对应值作为初值;
                if(k2==0){addr++;}
                if(k3==0){addr--;}
                if(k4==0){addr值写入eeprom;}
                display(addr);
}
if(num==1){同上操作,显示和读取eeprom值可以在c51教程找到}
if(num==2){同上操作}
if(num==3){同上操作}
回复

使用道具 举报

ID:982617 发表于 2024-8-23 14:30 | 显示全部楼层
在使用51单片机进行按键输入和数码管显示时,可以设计一个简单的状态机来处理按键输入和菜单的显示。以下是一个基本的实现步骤:

### 1. 菜单结构与变量定义
首先,定义菜单的页面和对应的参数值。假设你的菜单有以下页面:
- **P01**: 设置为初始页面,对应的值可能是0(或OFF)。
- **P02**: 设置为页面2,对应的值可能是123或ON。
- **P03**: 设置为页面3,对应的值可能是特定数字或状态(如OFF)。
- **P04**: 设置为页面4,对应的值可能是特定数字或状态(如ON)。

定义菜单页面和值的变量:
```c
#define MENU_PAGE_SIZE 4
#define OFF "OFF"
#define ON "ON"

char menuPage = P01;
char menuValue[MENU_PAGE_SIZE][10] = {"", "0", "123", "OFF", "ON"};
int currentValue = 0;
```

### 2. 初始化
在程序开始时,初始化数码管LED显示和LCD显示模块(如果使用)。

### 3. 状态机
使用状态机来处理按键输入和菜单显示。定义几个状态:
- **STATE_INIT**:初始状态,用于初始化和显示菜单页面。
- **STATE_NUM_UP**:值增加状态。
- **STATE_NUM_DOWN**:值减小状态。
- **STATE_CONFIRM**:确认状态,用于保存当前值并退出菜单。

状态机可以通过一个变量(如`currentState`)来跟踪当前状态,并根据按键输入改变状态。

### 4. 按键处理函数
```c
void processK1() {
    if (currentState == STATE_INIT) {
        // 菜单翻页
        if (menuPage < MENU_PAGE_SIZE) {
            menuPage++;
        } else {
            menuPage = P01;
        }
        displayMenuPage();
    }
}

void processK2() {
    switch (currentState) {
        case STATE_INIT:
            // 菜单值增加
            if (menuPage == 1) {
                currentValue++;
                if (currentValue > 9) {
                    currentValue = 0;
                }
                displayValue();
            }
            break;
        case STATE_NUM_UP:
            currentValue++;
            if (currentValue > 9) {
                currentValue = 0;
            }
            displayValue();
            break;
        case STATE_CONFIRM:
            // 保存值并退出
            displayValue();
            currentState = STATE_INIT;
        // 重置菜单值
        menuValue[menuPage] = menuValue[menuPage][0] + (char)(currentValue + '0');
        displayMenuValue();
        currentState = STATE_INIT;
    }
}

void processK3() {
    switch (currentState) {
        case STATE_INIT:
            // 菜单值减小
            if (menuPage == 1) {
                currentValue--;
                if (currentValue < 0) {
                    currentValue = 9;
                }
                displayValue();
            }
            break;
        case STATE_NUM_DOWN:
            currentValue--;
            if (currentValue < 0) {
                currentValue = 9;
            }
            displayValue();
            break;
        case STATE_CONFIRM:
            // 无需处理
            break;
    }
}

void processK4() {
    if (currentState == STATE_INIT) {
        currentState = STATE_CONFIRM;
        displayConfirmation();
    }
}
```

### 5. 显示函数
定义函数来更新数码管的显示。这通常涉及到硬件接口的使用,如定时器、串行通信等。

### 6. 主循环
在主循环中调用上述函数,并根据当前状态执行操作。

### 7. 运行流程
- 程序开始时,进入`STATE_INIT`状态,数码管显示菜单页面。
- 当按下K1时,切换菜单页面。
- 按下K2或K3时,在对应菜单值上加减。
- 按下K4时,确认当前值并退出菜单。

通过这些步骤,你可以实现一个基本的菜单设置功能,从按键输入到菜单页面和值的显示都有详细的处理逻辑。根据实际硬件和环境调整上述代码中的接口和细节部分。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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