在进入正题之前,我不得不重提一下自己当初踏上写技术博客这条路的初衷,一方面是作为自己技术积累的一个记录(习惯养成),另一个方面也是主要的目的之一是弘扬技术经验分享的精神(虽然自己还是言小甚微,呵呵)。至于让我再次想起当年初衷的原因则是前段时间在一个坛子里看到一个学生在吐槽声讨自己的技术成果被别人抄袭(主角竟然是俺们Kinetis),哎,如果说看到前半段我还可以同情一下的话,那看到后半段那句“飞思卡尔Kinetis几乎是学生在用,而STM32则主要是工程师在用”时我的态度已经严肃了很多,作为资深的Kinetis用户和“飞丝”,我不得不感叹一下,同学,这个世界还是被你想的小了些。至于后来我也在反思关于“抄袭”这个问题,虽然我的博客也被多次不标注原作者的转载过很多次(连俺写的【原创】两个字都没去掉),但是如果换个角度去看,受益的人群也由此更多了,在技术的道路上,大家都在摸着石头过河,技术的发展源于积累,积累到质变的时候形成创新,而创新则因此大多是站在前人的肩膀上。当然,我不是在鼓吹抄袭,而是在推动技术分享的精神,如果大家都本着开源分享的精神去做技术,在你为别人打开一扇门的同时,也会有另一扇门在等着你。
好了,吐槽完毕(主要是被那个同学给气的,呵呵,我前面说的有点含蓄了,实际我就是想说抄袭你说抄袭的事嘛,而上升到一个你不懂的层面装懂就不对了),开始进入正题吧。搞单片机的博友应该都会知道NMI的意思,即不可屏蔽中断,大多数半导体厂商一般情况下也不会舍弃这个管脚,其中我觉着有一部分是历史遗留问题,早期的单片机主频低,NMI中断用来保证特殊实时性的要求,而如今随着MCU主频和性能的不断提高(今年下半年ARM发布的Cortex-M7理论最高主频已经可以达到400MHz,接近MPU了已经),已经足以保证大多数中断的实时性。而NMI的属性问题即其中断不可屏蔽的特性也给原本不需要使用它的用户带来一些困扰,NMI管脚是低电平中断,因此如果不用它的话需要外部加上拉电阻(单片机内部有很弱的上拉,为保证可靠性,外部还是建议上拉4.7k或者10k电阻),更为头疼的是如果用户由于管脚资源不够想将NMI管脚复用成普通的GPIO而这个IO外接的元器件默认上电的属性为低电平则会造成单片机由于死在NMI中断中而启动不起来(因为NMI这个管脚上电默认是NMI功能,而单片机还没有来的及启动去重新配置该管脚为普通IO时就已经被迫进到NMI中断了)。呵呵,说了这么多,卖了半天关子,自然是有其解决办法的了,而且这个处理办法,飞思卡尔也已经为我们想好了,类似我之前写过的Flash加密的配置,如下图1,Kinetis可以通过FTFA_FOPT这个flash加载寄存器来禁掉NMI功能,而这个寄存器的值是在Kinetis复位之后立刻从Flash地址0x40D这个地址读取数据并赋给FTFA_FOPT这个寄存器的,因为我们只需要修改0x40D地址的数据即可以禁掉NMI功能。下面我就分别介绍下在IAR,Keil和Codewarrior这三大IDE环境下的配置方法:
1. IAR:
针对IAR开发环境禁掉NMI的方法,我之前在一篇博客中已经介绍过,请跳转至《Kinetis L系列将NMI和Reset管脚复用成GPIO需要注意的(http://www.eeskill.com/article/id/36592)》查询,当然,这篇中我一步到位地连RESET脚禁用也一块讲到了(突然想起姜文来,貌似最近一步之遥的影评比较差啊,听说他下一步电影就叫一步到位,呵呵),有需要的博友可以一块儿学习了,该篇文章主要以禁用RESET脚为例写的,换成NMI脚禁用的话,只要将配置数据由0xF7换成0xFb即可。
2. Keil MDK:
相对于IAR和Codewarrior,我觉着在Keil里禁掉NMI功能的方法是最简单快捷的。Keil里针对kinetis的启动代码部分是遵循ARM CMSIS软件标准格式来写的,而且在启动代码中加入了html语言,keil的编辑器可以自动识别到html语言并且将其直接翻译成配置选项,在工程中找到并打开相应的启动代码的汇编文件(一般命名格式为startup_MKxxxZx.s),如下图所示,用鼠标选中“NMI interrupts are always blocked”,即禁掉了NMI响应中断,修改完之后保存,重新编译整个工程,然后下载进去并复位芯片,可以看到以后就一直也不会进入到NMI中断里了,有没有So Easy的赶脚,呵呵。
3. Codewarrior 10.x:
在Codewarrior中禁掉NMI功能有点类似IAR的方法,需要手动去添加或修改相应的代码到工程中,不过在Codewarrior中我探索了两种方法,殊途同归啦,反正都是可以实现禁掉NMI脚的,就看个人喜欢去选择哪一种了,下面我分两种简单介绍一下:
第一种方法:
首先我们先找到Codewarrior中工程的链接文件(路径在本工程下Project_Settings->Linker_Files下的.ld文件),打开该.ld文件如下图,可以看到链接文件中已经为Flash配置域分配了16个字节的地址空间即m_cfmprotrom,其起始地址为0x400。继续往下看,该链接文件根据该地址空间分配了一个段即cfmconfig。
上述的cfmconfig段请记住它,我们下面只要在main.c中定义一个数组并把它指定分配到该段空间内即可实现对0x400开始的16个字节数据配置,具体配置方法如下,类似我很早之前在IAR加密那篇文章中提到的方法:
const unsigned int __attribute__ ((section(".cfmconfig"))) flash_config[] = //@ ".intvec" =
{
0xffffffff,
0xffffffff,
0xffffffff,
0xfffffbfe // disable the NMI pin and unsecure the chip
};
第二种方法:
实际上第一种方法给人的感觉是比较简单的,毕竟不需要修改任何代码,只需要在main.c里添加上述数组定义即可,不过它有一定的局限性,也就是说如果我们给该工程开了一定的优化等级的话,这个数组定义有被优化掉的风险,因为该数组没有在程序中被调用,编译器可能会把该段去掉以节省空间,我之所以只说可能是因为不同的编译器编译效果不一样,所以还不能下结论。所以我只好探索出另一种更为保险的方法,直接在链接文件中修改m_cfmprotrom地址空间范围的定义,如下,即把之前的.cfmconfig段定义注释掉,然后直接在m_cfmprotrom地址范围内写入4个32位的数据(我让其四字节对齐),这种语法格式是可以在GCC工具链中支持的,所以不必担心其编译问题。修改之后保存并重新编译,再下载进去之后,NMI中断就被屏蔽了,是不是这种方法更快捷呢,哈哈。
呼呼,好久没写了不大好控制,一下子又写了这么多,只能期待大家慢慢消化了,呵呵。休息一下,准备下一篇,敬请期待,未完待续~
jicheng0622的博客~