打印

高手讨论C程序

[复制链接]
3722|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
yesiqi|  楼主 | 2011-5-26 23:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
按键在单片机中已经习已为常,但是在做人机界面处理时,按键程序总是让我费时又费力的,大家在做工程中有没有遇到过,有没有好的方法,拿出来让我借鉴借鉴,我的一般方法就是:扫描——>>判断按键————>>置标志位————>>处理按键,这样在按键复用的时候总是有好多好多的标志位在判断,一不小心就留下了按键的bug,希望大家不令赐教!!

相关帖子

沙发
梅开二度| | 2011-5-26 23:14 | 只看该作者
按键,我建议你查找hotpower的“零耗时键盘扫描”,很容易根据自己的需要修改,满足自己的需要,可靠性好;如果要做成通用的,参考“农民讲习所”的例程,农民讲习所的程序你看懂了,自己再写个非常简单了。
很多优秀的按键程序或思路在21ic,看你自己愿不愿意去领悟了。

使用特权

评论回复
板凳
电子write_cai| | 2011-5-27 07:56 | 只看该作者
请问你说的讲习所在哪里可以看到 2# 梅开二度

使用特权

评论回复
地板
xixihaha0| | 2011-5-27 08:36 | 只看该作者
按键值 自己抽象出它的属性 ,包括 长安 短按 复合键按下,键值(比如用整型数据的不同位表示) ,然后中断扫描得键值,先放入fifo,再依次取出按键处理

使用特权

评论回复
5
sysdriver| | 2011-5-27 13:34 | 只看该作者
万能的键盘,至少我没有看过.

我的思路,整个按键扫描是弄成一个整体的,包括扫键,判断处理.没有用标记位.

if(bit_10ms==1) scankey();


scankey()
{
         key = scan();
         switch (key)
        {
                case 00:{ dealkey00(key);break;}
                //-----------------
       }
}

使用特权

评论回复
6
lxc806705| | 2011-5-27 14:55 | 只看该作者
我觉得每隔20ms扫描按键状态,并且用2个寄存器来寄存这个思路非常赞,主要是够简洁好理解

使用特权

评论回复
7
qbknight| | 2011-5-28 10:33 | 只看该作者
其实 克服这个问题 可以在键盘上下功夫

使用特权

评论回复
8
hotpower| | 2011-5-28 14:00 | 只看该作者
9
HORSE7812| | 2011-5-28 15:01 | 只看该作者
可以考虑一下使用状态机的方式

使用特权

评论回复
10
yesiqi|  楼主 | 2011-5-29 09:19 | 只看该作者
5# sysdriver
当按键较多时,这个程序不显示过大啊,还有同样的按键在不同的界面功能也不一样,怎么解决这个问题,

使用特权

评论回复
11
sysdriver| | 2011-5-29 12:53 | 只看该作者
5# sysdriver  
当按键较多时,这个程序不显示过大啊,还有同样的按键在不同的界面功能也不一样,怎么解决这个问题,
yesiqi 发表于 2011-5-29 09:19


这个问题要考虑到整个软件结构的问题,不同的结构有不同的解决方法。
像你说的,多按键多功能界面,同按键不同界面的,我看有人这样子弄过。

void main(void)
{
       system_initial();
       while(1)
       {
               key = scan();
               switch (key)
               {
                      case 0: {function_01(key);break;}
                      //-------function_02......
                      default: break;
               }
       }
}

void function_01(uchar key)
{
       function_01_initial();
       while(1)
       {
               key = scan();
               switch (key)
               {
                      case 0: {func01_key00(key);break;}
                      //
                      default: break;
               }
               if(key == 0xff){function_01_end();break;}
       }
}

这样的方法对维护或者扩展都是挺有好处的,每个function弄一个.c文件。

使用特权

评论回复
12
沧海一笑| | 2011-5-29 13:39 | 只看该作者
可以参考手册的经典例程...

使用特权

评论回复
13
yesiqi|  楼主 | 2011-6-27 08:51 | 只看该作者
看了好多说明,终于找到了和我想像一样的扫描方法,一会附详细的

使用特权

评论回复
14
ayb_ice| | 2011-6-27 09:51 | 只看该作者
可以参考PC键盘发码原理

使用特权

评论回复
15
hebeijiang| | 2011-6-27 15:57 | 只看该作者
我文化不多,理论套路都不懂,经常想到什么写什么,方法不固定,可能方法很笨,欢迎拍砖。
下面是其中一种:
//端口函数,每4mS扫描一次端口并处理
void FunPortIn(void) //这函数我放主循环里,完成端口扫描和去抖动。端口状态在In.Port中,键值的转换不在该程序。
{
    static U8 Time1mS;
    static U8 TimeS;
#ifdef          DebugPortIn
    U8 DbIndex;
#endif
    if (Time1mS == Time.T1mS) return;
    Time1mS = Time.T1mS;
#ifdef          DebugPortIn
    if (TimeS++ < 250) return;      //调试时改为每250mS扫描一次,方便打印数据观察
#else
    if (TimeS++ < 4) return;      //每4mS扫描一次
#endif
    TimeS = 0;
    PortScan();
    PortFilter();
#ifdef          DebugPortIn
    printf("\r\nInput.Port[0]=%x ;",Input.Port[0]);
    printf("In.Port[0]=%x ;",In.Port[0]);
    printf("Input.Filter=");
    for (DbIndex=0; DbIndex<MaxInPort; DbIndex++)
    {
        printf("%d; ",Input.Filter[DbIndex]);
    }
#endif
}

//读入端口数据,如果该端口禁止则固定为0
void PortScan(void)
{
    if ((InPort0 == 0) && (CheckInPort(In0) == Enable))          //In0
    {
        SetBit(Input.Port[In0/8],In0); //Input.Port保存临时端口状态,每Bit对应一位
    }
    else
    {
        ClrBit(Input.Port[In0/8],In0);
    }
    if ((InPort1 == 0) && (CheckInPort(In1) == Enable))          //In1
    {
        SetBit(Input.Port[In1/8],In1);
    }
    else
    {
        ClrBit(Input.Port[In1/8],In1);
    }
    //还有端口就往这加
}

//端口防抖动,采用的方法是:0的机会多就按0处理,1的机会多就按1处理
void PortFilter(void)
{
    U8 Index;
    for (Index=0; Index<MaxInPort; Index++)
    {
        if (Input.Port[(Index/8)] & (0x01<<(Index%8)))
        {
            if (++Input.Filter[Index] >= Input.SetFilter[Index]) //Input.SetFilter保存每位需要过滤的次数
            {
                SetBit(In.Port[Index/8],Index);       //输出1 //In.Port保存过滤后的端口状态
                Input.Filter[Index] = 0;
            }
        }
        else
        {
            if (-(--Input.Filter[Index]) >= Input.SetFilter[Index])
            {
                ClrBit(In.Port[Index/8],Index);       //输出0
                Input.Filter[Index] = 0;
            }
        }
    }
}

使用特权

评论回复
16
ahgao| | 2011-6-27 21:14 | 只看该作者
本帖最后由 ahgao 于 2011-6-27 21:23 编辑

复杂的肯定不是获取按键,而是如何与菜单交互。
某个可行的方法是,结构化菜单,设定一个菜单index结构,不要在扫描到按键后直接处理显示部分,而是根据当前菜单的index结构更新它;菜单显示模块通过读取index结构决定显示什么。这样做了你就发现,对于任何一个菜单状态,按键处理都是唯一的。没有定义的按键根本不会有反应。
这种方式的关键是菜单要结构化。如果你编写程序时有时间片和任务的概念,就更容易理解和使用它了,虽然它完全是运行在裸奔状态。

最后补充一句,程序的混乱一定来自于思想的混乱。先画流程图再写程序,磨刀不误砍柴工。

使用特权

评论回复
17
老鱼探戈| | 2011-6-27 22:39 | 只看该作者
哈哈,又来按键半月刊~

使用特权

评论回复
18
yesiqi|  楼主 | 2011-7-6 07:01 | 只看该作者
回复了这么多,没有一个成熟的思想吗?

使用特权

评论回复
19
yesiqi|  楼主 | 2011-7-6 07:03 | 只看该作者
这个问题要考虑到整个软件结构的问题,不同的结构有不同的解决方法。
像你说的,多按键多功能界面,同按键不同界面的,我看有人这样子弄过。

void main(void)
{
       system_initial();
       while(1)
       ...
sysdriver 发表于 2011-5-29 12:53
那请问这位达人,你有没有按键的连发设置呢

使用特权

评论回复
20
天凉好个秋| | 2011-7-6 11:29 | 只看该作者
把扫描键盘和处理键盘的程序分开

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:就侃单片机 技术博客http://blog.sina.com.cn/u/1681173590

2

主题

60

帖子

0

粉丝