那是始于2009年7月所发生的一个我和PIC32的故事。就在我的有关如何对16位PIC24微控制器进行编程一书刚刚出版不久,我听说Microchip公司一款新的32位PIC32微控制器已经出炉。该产品使用的是MIPS内核,同时还声称与16位的引脚以及PIC24系列产品外围兼容。对我来说这简直是太重要了!我立即起身去要到一个样片并刷新基于GNU的MPLAB C32 C编译器的beta拷贝。
我只是必须看一下这款新产品像什么。它仍然像是PIC MCU?它能不能在同样的演示版上工作?毕竟,我已经用C语言为PIC24写完了15章颇有价值的16位代码和例程。长话短说,在随后的不到一个月的时间里,我不仅完成了代码移植,而且已经开始利用所掌握的PIC32的经验书写一本新书了!
下面就是对一个月里所发生的事情的简要叙述。我喜欢从我遵守最好的设计准则并从阅读数据页开始讲述,如果说我从头到尾全面阅读了数据页,那是撒谎!实际上我所做的与你做的完全一样。打开装有以前PIC24项目的MPLAB集成开发环境,点击F10键立即创建。
二进制数字
一长串的错误列表出现在输出窗口中。令我惊奇的是,所报道的所有错误都明显地只与我的二进制注释(0b00000000),即C语言的一个非标准扩展有关。我试图编译我关于16位控制器一书前三章中的第一个代码例程。这是一段非常简单的代码,用C来说明I/O,精确定时以及流控制(用于循环)命令。我立刻决定将所有二进制文字转换成标准十六进制的注释(0x00)并观看结果,瞧!编译器和链接器马上解析出代码没有任何错误。
感觉很幸运,我决定继续“前进”,并在一些实际的硬件——即Explorer 16演示板上运行代码。我获取了一个PIC32插件式模块(PIM),并取代我的16位器件一书中普遍所用的PIC24 PIM。加电后我抱着非常怀疑的态度观察了几秒钟,竟然没有“冒烟”!然后我取了一块MPLAB Real ICE调试器和编程器连接到板子上。MPLAB IDE竟然很快识别出该工具并报告已发现PIC32连接到板子上。
在快速和自动的固件升级后,我便立即按下编程键,随后便是运行命令…。但却不工作!
我意识到板子上明显有什么问题,但一点也不像我所期望的那样。这里需要解释一下。在我的16位器件一书的前三章中,我利用C语言向读者提供了如何生成 “Hello World”这类例子。其中,我讲到传统的实现方法是,向终端发送一个字符串,但这在嵌入式控制应用中是不现实或者不合适的。而是采用一个“有趣的”方案,即制作一排8个LED,当把板子拿在手上并挥动时,灯将有节奏地闪烁。它将显示出有用的信息,这要归功于眼睛的自然成像持久性。实际上对此进行编码要比描述更容易。
不同时钟
事实上是,PIC32得到的I/O引脚和定时全部都是错误的。
对于这一点,通常我只需要噼里啪啦地翻开数据页并按我自己的工作方式来查找问题的根源。出现的问题是,PIC32时钟产生模块比16位器件一书中所用的 PIC24F要更加复杂一些。实际上,PIC32模块更像16位MCU系列中最新的PIC24F上的振荡器模块。同样,在PIC32结构中,绝大多数外设模块被连接到工作频率不同的彼此分离的外设总线上,这些频率低于系统时钟,这有助于功率管理,当然也有助于解决EMI问题。
我耐心找出如何使外设总线工作在与同一项目(16MHz外设总线)中PIC24F所用相同的频率。我还找出了可以执行的相同指令数,而执行频率仅为PIC24F所要求系统频率的一半,这是因为PIC32内核每个时钟周期上可以执行一个指令。
JTAG默认值设置为on
在解决了时钟问题之后,我快速地浏览了一下时钟模块。有5个时钟模块。看上去绝对与PIC24F完全一样,进一步回溯PIC MCU的历史,一直回溯到PIC16C74(大约1994)都是兼容的。我继续验证I/O端口:同样的结构,同样的引脚数,同样反映“历史”的寄存器名称,发现一个兼容型的轨迹也许可以一直延伸到最初的PIC16C54(大约1991年)。
最后我对A/D转换模块进行了一次快速检查,对于绝大多数PIC MCU初学者来说这是一个最难理解的外设。其输入连接到I/O口的上端(绝大多数16位PIC器件的PORTB),并且先加电,故除非你的配置正确,否则它不会使你的数字输入工作。显然它与PIC24兼容,因此我仍然无法解释LED行为异常的原因。
更靠近看,我发现有4个LED,要么从来不亮,要么就恒亮。于是,我又再一次翻开数据页来检查引脚图,最后终于发现了“元凶”:JTAG端口。
四线(E)JTAG接口被称为在线串行编程接口,是一个非正式的行业标准,它不仅允许边界扫描,而且还支持器件完全编程和调试控制。当然,这在引脚数很多的 32位芯片中是所期望的,PIC32在加电时通过默认的方式将这两个接口都激活了。如果为了利用一些PORTA I/O而不需要这些JTAG接口,则依赖应用程序来将其关闭。
自从我注意了JTAG接口后,我的第一个PIC32项目开始按期望工作,并发送出它的首个“Hello”,如图1所示。
图1:用PIC32产生字符串。
至此所学到的简单经验(振荡器配置和JTAG接口)迅速地证明了它们与我16位器件一书中前面各章节中绝大多数项目兼容性的关键,在随后几天的开发中移植都比较顺利。我利用UART与PC通信,用SPI接口与串行EEPROM通信,而利用Parallel Master Port与LCD模块通信。我利用A/D先读取电位器,然后读取温度传感器,演示了PIC32如何与模拟应用接口。除了模块的一些扩展功能以外,所有这些模块的工作都与我所预期的完全一致。我发现我的16位代码完全可以照用,几乎不需要任何的改变。
测量性能
在早期的一些日子里,我的好奇心促使我想知道PIC32究竟带来什么好性能。在我的16位器件一书的第四章中,即“Numb3rs”,我对执行基本的算术运算所需的指令周期数进行了统计,并将它们与各类整数和浮点类型进行了比较。这在时钟周期与所执行的指令密切相关的场合,如PIC24和dsPIC DSC内核中那样,这种方法是合理的。但在PIC32内核中,由于采用了MIPS的传统,为“比赛”增加了“难度”。每个时钟周期所执行的指令是可变的,因为当执行代码快于闪存额定速度(每30MHz只插入一个时钟周期)时可以插入等待状态,或者可以无关,这要归功于预取状态机(能够一次预取四条指令)。最后,激活一个高速缓存,进一步改进了高速性能。
PIC32的高速缓存使得周期数有点不可预测,也许变得没有意义。我好像觉得我从货物推车一下子升级到了一级方程式赛车一样!于是,我决定需要在32位器件一书中增加一章关于PIC32的性能调整内容。为了给PIC32加上重载荷,我找到我在大学读书时学习基本数字信号处理的一个老代码程序:即快速付里叶变换。我采用的是标准浮点结构,没有手工和编译器优化。另外还用了一个32 位定时器,让PIC32自己计时,随后我逐步地开始选用一些新的程序选项。
开始时,我激活指令预取,然后我找到高速缓存,再随后我通过人工方式调整等待状态。一开始性能改善极大,并且随着之后对配置进行进一步的细调,性能改善更多。最终,我意识到最佳的配置必须随应用定制,但必须由标准器件库中的SYSTEMConfigPerformance()提供一个好的起点。
学习外设库
这是我第一次使用“标准”外设库,也是这种爱/恨关系的开始。由于我在非常小型的8位器件上使用汇编进行代码开发已经许多年了,且通常都是需要采用手工优化客户代码,我基本上都是自己亲自工作,最终我开发出了一些自己的器件库。
这一次,在投放PIC32产品之前一年多的时间,我不仅移植了16位器件的库,还对它们进行了扩展来支持一系列新功能。我没有更多的理由-唯一理由就是我自己必须掌握并学会如何使用它们。参见用于一个使用该外设库的程序代码段的Listings1和2,见图2。
图2:代码移植时用于一个使用该外设库的程序代码段的Listings1和2。
通过利用这个新库,16位和32位应用之间的代码兼容“绝对”没有问题。即便是外设寄存器上的极小差别也可以通过应用代码完全消除。实际上,这使得一个应用在16位器件和32位器件上都可以运行,从而开发人员面向两种架构,却维护统一的代码基。
不过,虽然在器件数据页中对硬件控制寄存器名称已有注明(甚至每一位都很详细),但却没有所有的功能/宏名及其参数。很多时候,我发现必须将单个的包含文件与器件数据页进行比较,尝试着去猜测究竟有哪些控制位与一个特定的库参数相关。当利用最简单的库(比如I/O端口操作)时,这是一件特别麻烦的事情,对我来说,在这里,库抽象层的优点更值得质疑。
最终,我发现可以采取一个平衡折中。即可以采用传统的方法访问绝大部分的基本外设 (例如I/O端口和计时器),而在使用更复杂/新外设时才使用库。于是,我迅速通过了有关代码的几个章节,实际上什么都没有改。这些章节包括:SD /MMC接口,FAT16文件I/O甚至包括WAV音乐文件重放。
当我决定再深入地研究中断时,以及后来开始使用PIC32的新DMA模块时,这些库的好处就变得很明显了。
PIC32提供两种中断选择:一种是非常类似于PIC16/18 8位架构操作方式的单矢量模式(顺便指出,与RTOS也更加友好),另一种是更类似于16位PIC24 MCU和dsPIC DSC工作模式的多矢量模式。利用interrupt.h库来设置参数是轻而易举的事情。
是我开始尝试移植第12章中代码的时候了:“黑屏”确实是一件有趣的事情。用PIC24,我能够演示SPI端口是如何的简单,只需要三只电阻器,几个中断,以及一些创新就可以产生一个复合视频信号,特别是可以将任意的电视机转换成单色显示器。要产生一个视频信号,需要中断代码与外设之间的精密协调。实际上,因为即便是在输出定时上只差一个时钟周期,在显示器屏幕的左侧就会产生可见的抖动(所有的竖直线都变得像锯齿一样),故训练结果将是用于中断“决策”的一个理想放大镜,这是PIC架构传统上一直出众的特性。然而不幸的是,根据定义,指令预取和存储器缓存机制都是非确定的。
经过一段苦思冥想,我最终明白了。我在试图做不应该做的事情!32位内核是为了提高性能而设计的。其使命是C代码的运行尽可能地快,而把实时严格的工作留给外设。特别是,DMA外设是一个非常好的工具。
最终,我想出了如何利用定时器直接产生复合视频信号,并将DMA数据传输同步到SPI端口的方法。这种新方案提供了确定性的定时,而且还将CPU的开销减少了大约25%到5%。几个小时工作后,我完成了2D和3D视频演示并运行,还加上了动画,从高清显示一直到单显VGA(实例见图3和图4)。
图3:用PIC32所产生的3D图形。
图4:用PIC32所产生的不规则几何图形。
开发PIC32很快变成一件令人上瘾的事情,结果是很好的回报,那就是让我完成了一本有关32位器件新书的写作之旅!PIC32与早先的16-bit PIC24微控制器的兼容性是那样的无缝。这款新的MIPS内核的速度和性能给我留下了深刻的印象,大大扩展了任何先前的PIC MCU都无法实现的应用范围。
表1:PIC24F AD1CON寄存器的并排比较。