处理功能键

  功能键的问题在于,用户界面并非固定的,用户功能键的选择将使屏幕画面处于不同的显示状态下。例如,主画面如图1:

图1 主画面

  当用户在设置XX上按下Enter键之后,画面就切换到了设置XX的界面,如图2:


图2 切换到设置XX画面

  程序如何判断用户处于哪一画面,并在该画面的程序状态下调用对应的功能键处理函数,而且保证良好的结构,是一个值得思考的问题。

  让我们来看看WIN32编程中用到的"窗口"概念,当消息(message)被发送给不同窗口的时候,该窗口的消息处理函数(是一个callback函数)最终被调用,而在该窗口的消息处理函数中,又根据消息的类型调用了该窗口中的对应处理函数。通过这种方式,WIN32有效的组织了不同的窗口,并处理不同窗口情况下的消息。

  我们从中学习到的就是:
  (1)将不同的画面类比为WIN32中不同的窗口,将窗口中的各种元素(菜单、按钮等)包含在窗口之中;
  (2)给各个画面提供一个功能键"消息"处理函数,该函数接收按键信息为参数;
  (3)在各画面的功能键"消息"处理函数中,判断按键类型和当前焦点元素,并调用对应元素的按键处理函数。

/* 将窗口元素、消息处理函数封装在窗口中 */
struct windows
{
 BYTE currentFocus;
 ELEMENT element[ELEMENT_NUM];
 void (*messageFun) (BYTE keyValue);
 …
};
/* 消息处理函数 */
void messageFunction(BYTE keyValue)
{
 BYTE i = ;
 /* 获得焦点元素 */
 while ( (element [i].ID!= currentFocus)&& (i < ELEMENT_NUM) )
 {
  i++;
 }
 /* "消息映射" */
 if(i < ELEMENT_NUM)
 {
  switch(keyValue)
  {
   case OK:
    element[i].OnOk();
    break;
   …
  }
 }
}

  在窗口的消息处理函数中调用相应元素按键函数的过程类似于"消息映射",这是我们从WIN32编程中学习到的。编程到了一个境界,很多东西都是相通的了。其它地方的思想可以拿过来为我所用,是为编程中的"拿来主义"。

  在这个例子中,如果我们还想玩得更大一点,我们可以借鉴MFC中处理MESSAGE_MAP的方法,我们也可以学习MFC定义几个精妙的宏来实现"消息映射"。

处理数字键

  用户输入数字时是一位一位输入的,每一位的输入都对应着屏幕上的一个显示位置(x坐标,y坐标)。此外,程序还需要记录该位置输入的值,所以有效组织用户数字输入的最佳方式是定义一个结构体,将坐标和数值捆绑在一起:

/* 用户数字输入结构体 */
typedef struct tagInputNum
{
 BYTE byNum; /* 接收用户输入赋值 */
 BYTE xPos; /* 数字输入在屏幕上的显示位置x坐标 */
 BYTE yPos; /* 数字输入在屏幕上的显示位置y坐标 */
}InputNum, *LPInputNum;

  那么接收用户输入就可以定义一个结构体数组,用数组中的各位组成一个完整的数字:

InputNum inputElement[NUM_LENGTH]; /* 接收用户数字输入的数组 */
/* 数字按键处理函数 */
extern void onNumKey(BYTE num)
{
|| num==) /* 只接收二进制输入 */
{
 /* 在屏幕上显示用户输入 */
 DrawText(inputElement[currentElementInputPlace].xPos, inputElement[currentElementInputPlace].yPos, "%1d", num);
 /* 将输入赋值给数组元素 */
 inputElement[currentElementInputPlace].byNum = num;
 /* 焦点及光标右移 */
 moveToRight();
}
}

  将数字每一位输入的坐标和输入值捆绑后,在数字键处理函数中就可以较有结构的组织程序,使程序显得很紧凑。

  整理用户输入

  继续第2节的例子,在第2节的onNumKey函数中,只是获取了数字的每一位,因而我们需要将其转化为有效数据,譬如要转化为有效的XXX数据,其方法是:

/* 从2进制数据位转化为有效数据:XXX */
void convertToXXX()
{
 BYTE i;
 XXX = ;
 ; i < NUM_LENGTH; i++)
 {
  XXX += inputElement[i].byNum*power(, NUM_LENGTH - i - );
 }
}

  反之,我们也可能需要在屏幕上显示那些有效的数据位,因为我们也需要能够反向转化:

/* 从有效数据转化为2进制数据位:XXX */
void convertFromXXX()
{
 BYTE i;
 XXX = ;
 ; i < NUM_LENGTH; i++)
 {
  inputElement[i].byNum = XXX / power(, NUM_LENGTH - i - ) % ;
 }
}

  当然在上面的例子中,因为数据是2进制的,用power函数不是很好的选择,直接用"<< >>"移位操作效率更高,我们仅是为了说明问题的方便。试想,如果用户输入是十进制的,power函数或许是唯一的选择了。

  总结

  本篇给出了键盘操作所涉及的各个方面:功能键处理、数字键处理及用户输入整理,基本上提供了一个全套的按键处理方案。对于功能键处理方法,将LCD屏幕与Windows窗口进行类比,提出了较新颖地解决屏幕、键盘繁杂交互问题的方案。

  计算机学的许多知识都具有相通性,因而,不断追赶时髦技术而忽略基本功的做法是徒劳无意的。我们最多需要"精通"三种语言(精通,一个在如今的求职简历里泛滥成灾的词语),最佳拍档是汇编、C、C++(或JAVA),很显然,如果你"精通"了这三种语言,其它语言你应该是可以很快"熟悉"的,否则你就没有"精通"它们。

C语言嵌入式系统编程修炼之五:键盘操作的更多相关文章

  1. C语言嵌入式系统编程修炼

    C语言嵌入式系统编程修炼 2008-08-19 作者:宋宝华 来源:天极网 C语言嵌入式系统编程修炼之背景篇 本文的讨论主要围绕以通用处理器为中心的协议处理模块进行,因为它更多地牵涉到具体的C语言编程 ...

  2. C语言嵌入式系统编程修炼之一:背景篇

    不同于一般形式的软件编程,嵌入式系统编程建立在特定的硬件平台上,势必要求其编程语言具备较强的硬件直接操作能力.无疑,汇编语言具备这样的特质.但是,归因于汇编语言开发过程的复杂性,它并不是嵌入式系统开发 ...

  3. C语言嵌入式系统编程修炼之二:软件架构篇

    模块划分的"划"是规划的意思,意指怎样合理的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求.C语言作为一种结构化的程序设计语言,在模块的划分上主要依据功能(依功能进行 ...

  4. [读书笔记1]《C语言嵌入式系统编程修炼》

      大学前两年一直搞的是单片机,写的是嵌入式C语言程序,走过了不少弯路,现在感觉仍然在走弯路.有幸偶尔看到了这篇文章,深感自己以前写程序的时候存在很多误区.现写篇博客做下总结. 作者:宋宝华出处:天极 ...

  5. C语言嵌入式系统编程修炼之四:屏幕操作

    汉字处理 现在要解决的问题是,嵌入式系统中经常要使用的并非是完整的汉字库,往往只是需要提供数量有限的汉字供必要的显示功能.例如,一个微波炉的LCD上没有必要提供显示"电子邮件"的功 ...

  6. [读书笔记2]《C语言嵌入式系统编程修炼》

    第3章 屏幕操作   3.1 汉字处理 现在要解决的问题是,嵌入式系统中经常要使用的并非是完整的汉字库,往往只是需要提供数量有限的汉字供必要的显示功能.例如,一个微波炉的LCD上没有必要提供显示&qu ...

  7. C语言嵌入式系统编程修炼之六:性能优化

    使用宏定义 在C语言中,宏是产生内嵌代码的唯一方法.对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方法. 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的 ...

  8. C语言嵌入式系统编程修炼之三:内存操作

    数据指针 在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的MOV指令,而除C/C++以外的其它编程语言基本没有直接访问绝对地址的能力.在嵌入式系统的实际调试中,多借助C语言指针所具 ...

  9. [读书笔记3]《C语言嵌入式系统编程修炼》

    第五章 性能优化   5.1 使用宏定义 在C语言中,宏是产生内嵌代码的唯一方法.对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方法.   写一个"标准"宏MIN ...

随机推荐

  1. ueditor 添加微软雅黑字体 异常“从客户端中检测到有潜在危险的 request.form值”,解决

    使用ueditor往数据库添加文本内容时,如果字体有css样式, <,>," 这些字符会导致报出异常信息:从客户端中检测到有潜在危险的 request.form值 因为这些字符有 ...

  2. WPF 格式化输出- IValueConverter接口的使用

    以前在用ASP.NET 做B/S系统时,可以方便地在GRIDVIEW DATAList等数据控件中,使用自定义的代码逻辑,比如 使用 <%# GetBalance(custID) %> 这 ...

  3. java 函数形参传值和传引用的区别

    java方法中传值和传引用的问题是个基本问题,但是也有很多人一时弄不清. (一)基本数据类型:传值,方法不会改变实参的值. public class TestFun { public static v ...

  4. SqlSugar框架T4模板的使用

    一.T4模板说明 1.T4模板是用来生成Model层实体类的 2.文件后缀为.tt 3.需要修改配置主要有:引用的SqlSugar.dll的位置.生成实体类的位置及生成实体类的命名空间 4.T4模板生 ...

  5. [页面辅助] 最新的 PageValidate 类 (转载)

    代码 using System; using System.Data; using System.Configuration; using System.Web; using System.Text. ...

  6. SQL查询一些浅薄的结论

    一些简单的测试结论 在本机经过一些简单的测试,记录数6W条,得出以下结论,不同的硬件环境和数据记录数,可能会有不一样的结论 1.in, or, exists, like, not in , not e ...

  7. SQL打印全年日历

    数据库环境:SQL SERVER 2008R2 我之前有写过打印本月日历的SQL,里头有详细的说明.具体请参考前面的博文——生成本月日历. 全年日历只是在本月日历的基础上加了月信息,并按月份分组求得. ...

  8. SQL2008安装提示"Microsoft visual studio 2008早期之前的版本"解决(这是我认为最简单有效的方法)

    作者:冰封 日期:2013-10-18 原文地址:http://www.skywj.com/thread-9230-1-1.html 在安装SQL Server的时候提示 Microsoft visu ...

  9. 【BZOJ3456】【CDQ分治+FNT】城市规划

    试题来源 2013中国国家集训队第二次作业 问题描述 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得 ...

  10. 速卖通---发布商品aeopAeProductPropertys这个字段值报07004013的错误

    由于文档的说明很少,导致里面改填写那些值都是靠自己推敲出来,当然可以根据他们的错误提示了研究,他们的错误提示也给出了相关的帮助了, 例如通过categoryid的200000001获取到"i ...