原创 从 Kinetis 的 sample code 中的一个例程说起

2013-8-14 14:46 2015 18 29 分类: 消费电子

从 Kinetis 的 sample code 中的一个例程说起

 

[前言]
在前文"关于 FREESCALE 的 DEMO 中 PSOR与PCOR 操作的常见错误 "中, 我们讨论了 kinetis L series 的 Sample 中容易出现的, 关于 PSOR 的常见错误, 我们分析, 这种错误, 大致可能与未曾熟读 RM 有关.

在之后对 kinetis L series 的 samples 继续短暂的了解过程中, 我们发现参考例程其他的较明显的错误, 或者说不足疏漏之处.

因其常常表现为 uController 的新生容易发生的错误, 故我想称其为"初学者错误", 或者说是"初学者不足".

 

[例程]
让我们引 Blinky sample 中的, 开启 Interrupt 的例程作为例子, 如下图例1:

[图例1: Blinky sample 中的使能例程]

1.jpg

这个例程, 让我们觉得不能沉默以对.


因为, 作为 Freescale 官方例程, 可能类似的代码会被我们国内的初学者诸君, 作为效仿的对象. 从而产生错误的引导效果, 不利于我们工程师的自我完善和提升.


[可能的疏漏或错误]
这里我们想主要指出, 这段短短的例程中可能出现的 4 个不足:

(1) 首先, 这个 (irq%32) 浪费代码空间并影响效率.
    -- 其本质原因, 可能存在着逻辑上混淆不清.

(2) 其次, IRQ的值域为: [0, 31]. 而该 code sample 却允许对 32 的计算.
    -- 其本质原因, 应该是未作越界(边界)检查. 并未仔细阅读 Register bit field定义.

(3) 第三, 对 irq=31 时的可能情况, 也就是 (1<<31) 的情况, 毫无敏感.
    -- 这里毫无敏感的意思, 大致反应为: (a) 对有符号数与无符号数区别不敏感, (b) 对左移操作不敏感.

 

(4) 第四, 正如同我们在 PSOR 中分析到的 |= 错误, 在这段简单的例程中, 仍然存在 ICPR 在 bit 被 write 0时无任何影响,  ISER 在 bit 被 write 0 时无任何影响. 都应该改为 =.

 

[讨论(1<<31)的特例]

让我们用例子来讨论这个 (1<<31):
(1) 首先, IAR 中定义的常量, 都是有符号的 int 类型.

这个前提为大家公认, 但是我没有找到出处```
麻烦读到此处的同行, 能给我一个出处链接, 或者是 IAR help documentation 中的说明. 蟹蟹.

(2) 有符号数 1, 在左移 31 bit 后, 将导致符号位被置1, 也就是计算得到一个负整数.

如果我们只看这里 NVIC_ICPR |= 1 << 31; 似乎没有影响到最终逻辑结果.
但是, 其真实原因是, 该负整数被强制转化为 unsigned int 的结果, 也即是最终正确的 0x80000000.

但这并不表示, coder 清楚这个结果是由强制转换产生,

从这段代码本身, 我们合理判断该 coder 将分不清下面代码的执行结果, 也就是容易犯一些"初学者错误".

比方说: 我怀疑该 coder 会毫无自觉写出下面的代码:
if( (1<<31) > 1 ) { do what he wants; }

显然我们知道, 这片代码永远不能为真, 去执行 what he want to do.

 

(3) 根据图例2, 我们对 (1<<31) 进行详论:

[图例2: Allen 随手编码给出3个例子讨论 (1<<31) ]

2.jpg

我们把3个例子的结果, 都有注释, 可以见到:
(a) (1<<31) 的值, 不是正数 0x80000000, 而是负数 -2147483648.
(b) (1u<<31) 的值, 才是 0x80000000.
(c) 条件比较语句, 首先是两边转化为同类型才能比较. 由于无符号数优先级高于 int, 故 (1<<31) 被强制转化为 unsigned int, 也就是 0x80000000, 用于比较, 导致和 sample1 的结果截然不同.

上面3个例子, 清楚表明了 (1<<31) 的值为何(如果我有任何错误请告诉我).



[我们修改的代码]
因此, Allen 尝试修改这个例程如下, 见图例3:

[图例3: 被修改后的例程]

3.jpg
 
[结论]
我们通过对一个 enable interrupt 例程的修改, 讨论了 firmware programmer 可能容易犯的问题, 主要有:
(1) 不能熟读 datasheet, 了解 register 的具体用法.
(2) 代码逻辑混乱.
(3) 忽视边界检查.
(4) 对有符号和无符号数区别不够重视.
(5) 不了解左移时牵涉符号位的特例.
(6) 不清楚在条件比较时强制转换现象.

上述问题, 一般多见于 uController 的初级选手.

作为公司新晋员工, 或者任何致力于 MCU code 实现 application 的新进同行们, 可以将上述错误作为范例保存而自省.

另外, 我们说不定应谨慎检阅各种 Freescale 给出的 kinetis L series 的参考 samples, 反复测试, 可能避免出货后造成不可预料之损失.

Allen zhan
2013.8.14
Release On EETC

文章评论11条评论)

登录后参与讨论

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.
相关推荐阅读
allen_zhan 2023-02-27 19:08
对"三极管"译名由来的探讨
想讨论一个有意思的话题:今天中国大陆的电子业界, 为何将 BJT 称呼为 "三极管"? 或因其象形, 前辈自行进行随意的不严谨定义么? 带着疑问我们做了一下延伸查阅, 或得出这样的结论, 即中译名"三...
allen_zhan 2023-02-19 18:15
对知乎提问"为何三极管的一个PN结工作在反偏"的回复
将这个回复, 也发表在博文中, 作为自己的一个学习笔记叭.知乎问题: "三极管里面的PN结相当于二极管,为什么里面PN结加反向电压也能导通?"我的回复:首先, 二极管的"反向"概念, 容易给初学者某种...
allen_zhan 2023-02-18 10:17
从肖特基二极管到PN结与三极管
最近数个工作日的兴趣是回顾电子基础器件的发明/发展历史, 期待夯实技术基础的底蕴. 在学习与搜索资料的过程中, 顺便对知乎的一个同学的基础问题, 进行了回复. 不小心回复一下就成了千字文, 觉得挺有趣...
allen_zhan 2023-01-28 11:53
微功率 ISM 频率探讨相关文档组总结
不知不觉, 自开启关于微功率频率的话题起, 即从第一份文章写就到今天总结之日, 已经接近 10 个工作日左右. 早先的想法是对工程界未来的微功率设备相关项目, 从项目规划开始, 对选择系统, 频率, ...
allen_zhan 2023-01-27 22:50
关于 LoRa 应用场景的讨论
说明: 本文中斜体部分表示来自公告文件的部分内容剪贴或合并整理.1. "第52号文" 对 470MHz 的约束引自 如下:(四)民用计量仪表限在建筑楼宇、住宅小区及村庄等小范围内组网应用,任意时刻限...
allen_zhan 2023-01-25 13:24
ISM 频段中 2.4G 与 5.8GHz 设备的使用与限制
说明: 本文中斜体部分表示来自公告文件的部分内容剪贴或合并整理.1. ISM 频段定义中的 2.4G 与 5.8GHz正如同 文中确定的, 2.4G, 5.8GHz 属于中国大陆 ISM 频段的定义...
我要评论
11
18
关闭 站长推荐上一条 /2 下一条