从 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 中的使能例程]
这个例程, 让我们觉得不能沉默以对.
因为, 作为 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) ]
我们把3个例子的结果, 都有注释, 可以见到:
(a) (1<<31) 的值, 不是正数 0x80000000, 而是负数 -2147483648.
(b) (1u<<31) 的值, 才是 0x80000000.
(c) 条件比较语句, 首先是两边转化为同类型才能比较. 由于无符号数优先级高于 int, 故 (1<<31) 被强制转化为 unsigned int, 也就是 0x80000000, 用于比较, 导致和 sample1 的结果截然不同.
上面3个例子, 清楚表明了 (1<<31) 的值为何(如果我有任何错误请告诉我).
[我们修改的代码]
因此, Allen 尝试修改这个例程如下, 见图例3:
[图例3: 被修改后的例程]
allen_zhan_752827529 2014-4-24 15:40
1989tie_959541171 2013-8-21 12:02
allen_zhan_752827529 2013-8-19 17:57
我踌躇着不知道该如何回答您...如果是因为您对我们讲述的领域(freescale 的 kinetis L ,cortex-m0+)不熟悉, 只是就语法论语法, 那么您是对的, 我为自己的轻率发言表示歉意. 如果是上述专业方向, 见3楼4楼的讨论.
用户1454004 2013-8-19 15:18
allen_zhan_752827529 2013-8-15 17:41
另外, 我还想说一句啊, 老兄, 十年入门啊... 十年你都不能理解这个 register 的定义... 正好就是我说的 "初学者" 概念啊... 正好就是我在这篇 blog 中疾呼的, 不能随意模仿低阶的编程水平, 不能跟这个 sample 中的 coder 想当然的随手编程 |= 这样...老兄... 我们中国工程师不能落于世界平均水平之后啊...
allen_zhan_752827529 2013-8-15 17:39
用户1454004 2013-8-15 17:11
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
allen_zhan_752827529 2013-8-15 12:30