一个需要关心的问题是 位是否足够表达足够多的键
任何一个系统,32位变量不在话下,即使8位机如51,long 或者 long long也可以是32位,甚至64位。
当然有些系统可能会有更多的按键,甚至多达百来个以上,这里先不考虑这种情形,何况在FreeUI里其实我只需要16个,大多数情况,二三十个就绰绰有余。
接下来是实现 最基本的 按键扫描。
static int keybit = 0;// 其实 测试还应该包括 long int long long的区别对待#define MAX_KEY_BIT 32void size_of_keybit(void){ static size_t size; size = sizeof(keybit);}//公平起见,操作都是全部写1void scan_key(void){ int i; keybit = 1; for(i = 1;i < MAX_KEY_BIT;i++) { //keybit |= 1<<i; keybit <<= 1; keybit += 1; // 这个速度更慢,比第一种方法要慢33%左右 }}
作为对比,考虑一下 以往的方法:
static char keyvalue[MAX_KEY_BIT];void scan_key2(void){ int i; for(i = 0;i < MAX_KEY_BIT;i++) keyvalue[i] = 1; }
结果:
我是在 stm32f030r8t6 下测试的,用的IAR。
编译后通过查看反汇编代码。
得出的结论是(当时我是用long long定义 keybit,所以实际是64位,当然 keyvalue数组也是64元)
key_scan() 生成的代码长度 是 38个指令
key_scan2() 生成的代码长度是 93个指令
两者之比 约为 2.58
但与想象的有点出入的是。
两者的运行时间 却是 2:1,后来猜测,这可能和每次都执行了一个 移位 和 一个 位或操作有关。
看来,和这个相比,取数组的偏移计算,在底层反而是微不足道的。
后来,改成32位后,时间差别还是 为 3:2.
所以结论是:
使用位的方式,节省存储器,数据和程序 存储空间 都节省。
但是时间上,却要慢一半到一倍。
这种时候,到底是牺牲时间换取空间 还是相反 完全取决于你。
毕竟在大多数情况下(仅仅只有二三十个键的话),空间和时间上都省不了太多东西。
注:
我时间的测试方法是:
使用逻辑分析仪(当然也可以是示波器)。
然后把一个IO 写高写低,把这个操作插在要测试的函数调用前后,即可。
最后扯一点:
其实这种测试 和 对按键 的抽象方式 都有点过细。
我现在也很少再纠结这么底层的封装问题了。
我之所以这么干,一个是为了实际测试运行时间的差别。
另一个就是,按键看起来简单,其实玩起来大有可为。
然而,许多时候,不管按键多复杂——
比如说连按啊,长短按啊,是松了再触发还是 按下就触发啊,其实这些都可以 放后处理,而嵌入式/单片机程序,最麻烦的地方,无非是 与硬件有关,所以,我就直接用 一小片内存,把硬件上的键值映射过来了。
这个地方,只要随便一个定时器中断或者循环体里,循环调用 这个 扫描函数,就能得到键值。
另外啰嗦一点就是:
大多数时候按键需要计次消抖。
所以,用位还是有点麻烦,最终还是要用一个数组来计次。
如此说来,就等于回到了我平素的写法了,合二为一。
突然想知道你们是怎么写读键程序的——因为我最近在移植一个其他项目的UI,突然发现别人的想法和我的想法居然可以相差这么远,一开始我是有点抵触的,只是因为限于时间不得不硬着头皮,但后来随着半改半移植,也渐渐理解和接受了这种写法。
我读取键值是给每个按键用一个变量代表键值,使用定时器设置一个扫描标志位,每隔一段时间置标志位,然后在主程序中的while大循环中不断判断标志位,然后去更新键值,以供之后的程序使用。