原创 《删繁就简-单片机入门到精通》答读者键盘扫描问题

2010-3-13 16:01 2306 13 16 分类: 消费电子

Q:
楼主书中的内容还有需要改进的地方,
1:如果将 Pn.x 口设置为开漏输出,则 Pn.x 绝对不会短路,
   也就用不着将其它 Pn.x 设置为输入状态。
2:即使 Pn.x 只能设置为推挽输出,也不需要 Pn.x 输出高和低两个状态,
   只需将当前 Pn.x 输出低状态就够了,其它的 Pn.x 则设置为输入,
   此时 Pm.x 为低表示有按键按下,为高表示无按键。
3:加二极管来防止四点短路确实可行,但如果由于成本或硬件原因不适合加二极管,
   那么还有软件的方法来对四点短路进行判断(电脑键盘就是这么做的),虽然不能避免
   四点短路,但能发现四点短路,一般来说这足够了。

A:
1.你说的设置为开漏输出,我的理解是当前IO口为open drain结构,此时将IO口内部的上拉电阻关掉,不知我这样理解是不是和你的说法一致。如果相同,你的这种方法只是针对部分单片机有效,如果单片机IO是三态门结构,是没有内部上拉电阻进行选择的。另外我的主要目的是想让大家知道存在这样的风险,在实际应用中需要引起注意,我给出参考解决方法不代表这个方法是唯一可行之道,无论将其它IO口改成输入还是将内部上拉电阻关掉,都是通过设置避免不同IO在连通时出现一个输出高另外一个输出低的情况。
2.呵呵,是不是觉得我在文中要求输出高和低没有必要,好像是多此一举?这样做是有一定好处的,我在文中特意没有做相关说明,就是希望有你这样看过之后进行认真思考,并能提出自己的意见,这样才能加深技能技巧的印象。你这个问题质疑得好,为什么需要输出高和低两种状态,是为了避免其它错误,比如输入的IO口与地或者电源之间短路,只输出一种状态就可能做出错误的判断,假定Pm.0与地短路,是不是就会错误的判断成Pm.0上有键按下?
3.在文中已经说了二极管会增加成本,实际应用中可以通过软件方法判断多点按下将按键忽略

Q:
我说的开漏输出可以有上拉电阻,也可以没有上拉电阻。我认为用开漏输出扫描键盘是一种简便的方法,如果单片机有开漏输出功能的话,可以考虑采用。
输出高和低两种状态对判断Pm.x对地短路是有效果,不过我认为出货的产品一般都把硬件错误解决了。看来,用不用输出高和低两种电平,这没有绝对的答案,还是看自己的取舍了。

A:
看来我对你所说的开漏理解是相同的。在前面回复中已经明确说明,如果解决的方法有多种,不是说非要用某一种才可以,如果单片机支持你说的功能选择,一样可以解决问题。书中内容的目的是提醒大家需要注意有短路的可能,应该在程序中加以避免,实际上有的单片机的IO不支持输入输出选择,如果是这样的单片机那书中的内容就完全是错误的,对于这类细节,是需要读者自己去理解的。
这一点再多说两句,对于单片机内部的上下拉电阻,许多时候芯片资料对介绍并不清楚,这个电阻有多大,就是介绍也只是一个比较笼统的参考值,对于某些特殊电路,在外面另外加上下拉电阻的做**更稳妥,这样当出现多个电阻并联的情况时设计人员可以更精确的预估电阻值对电压的影响。我用一个极限例子来进行说明,有10条IO口都设为输入加内部下拉,这10条IO会同时读某一个信号量,但这个信号的电流驱动能力并不高,通常单片机内部下拉电阻为几十K,假设为47K,这样10条IO并联等效电阻就只有4.7K,这个电阻有可能把信号幅度拉低,比如是5V信号,4.7K电阻需要信号有1mA的驱动能力,如果其实际驱动能力值有0.5mA,会是什么样的后果?
做产品开发你所说的认为硬件把错误都解决了的想法不可取,合作是要相信别人,但绝对不能完全依赖别人,最好的做法是在条件允许的情况下,将自己的工作部分考虑更周详,更加稳定可靠。
还是你举个例子来说明你这种想法的问题所在,现在需要你写一个供别的同事实用的函数。
int data_copy(char *src,char *dec,int len)
按你的说法,只要在函数内部完成数据复制功能就可以。如果只是站在完成任务的角度,你这么做就已经可以,但绝对不能说你这样完成是高质量。
实际上如果有可能,应该在函数内部对参数做出一定保护,象int len,正常情况使用的同事会给出正确的值,但某些意外因素导致这个值被错误的设置或修改为小于0的值,你想想没有保护是什么样的后果?再比如目的地址,被意外指向系统区域,也是不允许的。

PARTNER CONTENT

文章评论3条评论)

登录后参与讨论

allen_zhan_752827529 2014-4-24 15:40

补充,我的改写的方式,违反了 MISRA-C:2004, 规则 14.7. 因为我个人喜欢简洁而没有使用嵌套的 if. 如果您的团队遵守上述规则, 则应注意此处并按退出点要求, 进行修改.

1989tie_959541171 2013-8-21 12:02

首先,关于使用|=还是=,这个有些像通用与专用的区别。这里因为Freescale这个MCU的特性,使我们可以直接使用=,而不用顾及修改其它位。现在越来越多的MCU是这样的了。而|=应该是通用的写法,不会出错。从效率上说=更好,从易读性上说|=更容易理解。如果代码大小不是极为苛刻,可以先用|=,使别人容易理解。如果使用=,也可以加上注释,声明这样做的原因。也使别人更容易理解我们的代码,不至于误解,而修改我们的代码。 其次,关于有符号无符号,这个确实是个大问题。很多时候,我们没有重视这个,都采用默认的情况。是因为我们涉及的都是同一个编译器,可以认为是一样的。但是,如果是不同的编译器,不同的MCU,同样的char,int就可能不一样。根据C标准,char,int的默认类型,是编译器决定的。换个编译器就可能不一样,根据《编程精粹》中提到的,对于未定义的特性,最好避免之。而这个问题自己也曾经遇到过,如果想不到默认类型是signed,还是unsigned,调试起来会很崩溃。所以这个很赞同,博主这种写法。 再者,如果要写出性能好的代码,芯片手册是不可避免要熟读。这一点,也是不争的事实。 还有一点,代码的性能和可读性一般成反比,这个应该值得我们在写代码时考虑。并进行一定的折中。

allen_zhan_752827529 2013-8-19 17:57

我踌躇着不知道该如何回答您...如果是因为您对我们讲述的领域(freescale 的 kinetis L ,cortex-m0+)不熟悉, 只是就语法论语法, 那么您是对的, 我为自己的轻率发言表示歉意. 如果是上述专业方向, 见3楼4楼的讨论.

用户1454004 2013-8-19 15:18

我倒从来没认为我在世界平均水平之前,不过就事论事而言,我还是没理解您的register是指那一部分,只能根据我所学的最基础的科目来理解来阐述:一般register在编程里,一种是指cpu的运算单元,比如v0-v7,参与逻辑运算;一种指的是供外设操作的部分,一般挂在地址/数据总线上;这两部分共同特点是在电路上使用的是D触发器。从回复上看,您指的可能是外设部分,暂且称之为ram型寄存器,不管是RISC架构还是CISC架构在|=、&=操作上都是先读后写,只不过risc在这点上尤其突出,毕竟从它一问世就把优化的责任推给编译器,唯一进步的是现在的电路设计可以有更高的裕量在设计时可以在规定的clk内送出结果;编译器设计时本着与人方便的原则将常用的语法习惯跟实际电路结合起来,好在cpu结构种类不多,变化的部分在汇编常常采用宏定义封装,事实上这个代码反汇编一下查阅对比就可以理解了,除非是一个WRITE ONLY的寄存器,那样在实际应用中又需要真正的ram来作为缓存,以前台湾人做4位机8位机为了省成本经常这么干;只有您讲的int转uint是需要考虑的,如果愿意研究,事实上也可以用一些编译器选项强制完成,不过那属于费力不讨好的划不来的方法。如果是运算单元的寄存器,一般C编译器有个约定俗成的习惯,会先把所有函数中用到的变量当成RAM,统计总共需要多少,然后根据不同的优化条件把一些单元分配给运算寄存器,再进行优化;原始的不带优化的编译器就只是把C当成汇编的助记符,非常有意思的是这个例子的输入irq不管在现代编译器还是原始编译器都会被分配成运算寄存器,前面讲的在规定clk送出结果大部分CPU芯片架构设计时都是首先完成这部分,也就是单指令可以完成,arm体系还好,很多CPU单指令未必是一个时钟周期。 严格来说,我这连初学者都算不上,还在门槛外面呢,只能根据一些以前学校学的大部分早还给老师后剩下的一点点基本功推演

allen_zhan_752827529 2013-8-15 17:41

另外, 我还想说一句啊, 老兄, 十年入门啊... 十年你都不能理解这个 register 的定义... 正好就是我说的 "初学者" 概念啊... 正好就是我在这篇 blog 中疾呼的, 不能随意模仿低阶的编程水平, 不能跟这个 sample 中的 coder 想当然的随手编程 |= 这样...老兄... 我们中国工程师不能落于世界平均水平之后啊...

allen_zhan_752827529 2013-8-15 17:39

坦率地讲, 你的观点没有错, (irq%32) 不算错误. 它算浪费与冗余, 我们喜欢称呼它是错误的原因, 可能潜意识我们拒绝这种冗余代码, 在微控世界中尤其不可接受. 至于 |= 我在回答上一个 galali 同行的讨论中, 我们表达的观点已经很清晰了, 请仔细阅读. 你需要仔细阅读 register 的 bit field 的定义. 你的理解是错误的. 针对类似的 register 的 |= 是不可取的.

用户1454004 2013-8-15 17:11

不认为 irq%32 这个是错误, 1.首先 参数irq是个32位的int类型, 1 <<(irq %32)的运算顺序是 step1: irq % 32 -> vtemp1 step2: 1<vtemp2 step3: vtemp2 -> 目标地址 前两个步骤均是在32位框架内计算的,只有在结果存入会根据目标寄存器类型是u8/u16/u32进行边界截取操作 2. irq %32 看起来好像耗费时间,事实上由于%运算后面是个常数,编译器优化选项会自动将它调整为irq&(0x1f) 3. |= 运算是因为要保留其他isr的状态值,如果写成=,其他该中断的不中断,就是错误了。

allen_zhan_752827529 2013-8-15 13:56

你说的很对, |= 与 = 意义是不同的. 其原因就是在 register 的定义中有体现, 你可以看看我其他一份文章里面有提. 对于很多 registers 来说, write0 代表意思不是 "disable", 不是"set 0", 而是"什么都不做, 什么都不影响". 也就是说, 不管是否原有 resigter 的某个 bit 是否为1, write 0 不会影响到这个bit, 也不会将原来为 1 的改变为0. -- 这就是我们说, 为什么此处 coder 犯了初学者错误的原因. |= 带来了代码尺寸的浪费, 效率的降低, "边缘效应"就不提, 我们也不知道可能会带来什么影响. 所以说起来, 为什么官方的 team 没有仔细研读官方的 datasheet 是好奇怪的事情. 但是这不关我们的事儿, 我们只要知道这个 |= 是一种彻底的浪费, 以及反应出编程的初学者痕迹即可.

用户1320373 2013-8-15 13:42

1. 操作符 |= 改为 = 后,意义可就大不一样了,不会影响到其他位吗? 2. 该函数明确定义为enable_irq,按编程者的意图,只需使能某个irq(也就是置位某bit),需要考虑 置位 和 清零 两种情况吗?

allen_zhan_752827529 2013-8-15 12:30

Thx. Emma.
相关推荐阅读
daishangju_162733976 2015-12-19 20:46
个人所见植保无人机
      实在是太久太久没有了上来了,在过去的这段时间里,一些不方便说的原因,还一些方便说的原因,缠绕在一起,让自己封闭(不好意思用蛰伏这个词)一年多,不单是博客没有来,就是邮箱都很少打开。...
daishangju_162733976 2014-10-23 19:48
微观经济1409
位于东莞市凤岗镇的一家小吃店,旁边是一家酒店,老板去年请了7个工人,每天销售5000~7000,今年9月只有1个工人,销售肯定1000以内。   ...
daishangju_162733976 2014-10-23 17:27
市场去哪儿了
好像是从2008年开始,但凡是市场情况不好,总是能见到是经济危机导致欧美市场疲软的解释,看到电视里面安然轰然倒下、华尔街员工茫然抱着纸箱的画面,我对这个解释也是深信不疑。转眼五、六年过去,可是市场...
daishangju_162733976 2014-09-10 14:29
一名电子工程师在深圳的迁移路线图
1999年中,来到宝安区翻身村,在某电子厂工程部当小弟混饭吃,当时小霸王系诸多好汉豪杰正在那边分猪肉。(今天是WWW要搞的前海CBD)   1999年末,转往福田区华强北,在某电子公司开发...
daishangju_162733976 2014-09-05 16:37
另眼看客户“你自己去做,我一定支持你”这句话
我们常常看到或听到这样的事,小X在某行业中打工,经过一番努力,做得还不错,这时候就有客户说:“小X啊,你自己去做吧,我一定支持你”,这个时候小X往往都是已经在考虑是不是要自己出去单干,听到这样的话...
daishangju_162733976 2014-07-03 16:47
谁是优秀的职业经理人?
LEE是公司元老,在公司上下眼里,他能力超凡,数次于水火中力挽狂澜,没有人不承认他是一名福将。LEE有多厉害呢?他主导的产品成功率大约为三分之一,除了他自己,没人知道他是如何选定产品的,甚至他自己...
EE直播间
更多
我要评论
3
13
关闭 站长推荐上一条 /3 下一条