本帖最后由 lishutong 于 2012-11-3 13:50 编辑
最近需要为TKScope仿真器增加对Cortex-R4内核的支持。目前我能找到的内核为Cortex-R4的芯片只有TI的TMS570系列和RM4系列。其中TMS570系列主要用于汽车电子方面,最大频率达180MHz。TI官方目前已经推出了4款基于TMS570的开发板,而我们部门购买了其中的TMS570LS31x Hercules 开发套件-TMDX570LS31HDK。经过努力,在调通TMS570内部的ICEPick后,我们的仿真器已经成功支持在Keil、IAR下仿真该芯片。
TI的这块板是我所见过的所有开发板中最具有特色的。其特色之处并不在于开发板硬件本身的设计,而在于它所提供的一系列工具和演示程序。在这些开发辅助工具和驱动库的易用性和功能完善性上,TI做得最为出色。由于没有相机,所以从该款开发板的手册中截取了这块板的正面图,请看下面。
这块板采用BGA封装的TMS570,板载性能较弱的XDS100v2 JTAG仿真器,以及一些传感器件接口。配套一张光盘,光盘里面包含30天评估版的CCS v4、Demo程序、HALCoGen、nowFlash等。
首先来看下演示程序。在安装完光盘中的安装程序后,可以在Windows的开始菜单中找到Demo的执行菜单。运行后,会弹出下面的界面。与别的开发板厂商不同的是,TI提供的Demo程序并不是Keil、IAR等工程文件,而是直接运行效果演示。
如果选择LED Light Show,相应的Demo固件会首先通过仿真器下载到板上,然后切换到LED Light Show演示界面。LED Light Show演示界面做的确实费了些功夫,估计是用画PCB软件生成的3D图片合成的。界面上有两个三色LED,鼠标在任意一个颜色上勾选后,板上的LED就会发出对应颜色的光。这个Demo需要上位机软件与XDS100v2 JTAG通信,不知道Demo固件参与了这个操作过程没有(没有参与应该也能实现?)。这个Demo看起来似乎没有太大的实用价值,不过倒还是让人觉得做得挺牛B的。
工具方面,这块板也配套比较多。其中nowFlash是一款比较小巧的Flash烧写软件,专用于烧写片内Flash,可以实现一些基本的擦除、编程、烧写、校验、保存操作,不支持更复杂的功能。这款软件通过XDS100v2 JTAG仿真器烧写,烧写的速度比较慢,只适合开发人员简单使用,不适合大批量烧写,
试着用了一下,速度不怎么样,擦除一个32KB的块估计得1秒。个人感觉,它的烧写过程可能是使用JTAG直接写Flash控制器的相关寄存器、而不是在芯片的RAM中加载并运行烧写算法来实现。从烧写过程中打印出来的信息来看,该软件基于不同的内核、Flash控制器、大小端提供不同的算法,支持的TI芯片种类还是比较多的。
FMzPLL是一款图形化的PLL配置工具。在一款芯片中,时钟系统模块算是比较复杂的,而其中PLL配置是最难的。配置过程需要进行计算,并且还需要注意配置参数是否在合法的范围内。FMzPLL相对而言提供很直观、易用的操作界面,用户可以进行各种配置尝试并立即得到配置结果,非常省时省力,而且也不易出错。
TI主推的软件开发环境为CCS V4,基于Eclipse。编译器使用GCC,调试插件使用的是TI自己开发的。相对于Keil、IAR,Eclipse的编辑器用起来是比较舒服的。
其实我觉得TI做的最牛B的软件是HAL Code Generator,也就硬件驱动库代码自动生成的软件,非常人性化的一款软件。现在很多芯片生产厂家为了降低开发难度,都提供了驱动库,比如SST、NXP等。这些驱动库都是直接以源码形式提供。虽然使用上比直接使用寄存器的难度要低很多,但是仍然不够直观,并且为了综合各方面的考虑,驱动库函数会设计的比较复杂。
我个人认为在使用某个芯片的模块时,难度最大的部分在于如何对该模块进行初始化配置。TI很牛,直接将硬件整个TMS570的硬件框图给做成了界面,并且细化到各个模块的内部结构上。这样用户就不需要去查手册了解有哪些寄存器,寄存器的各个位含义是什么。通过界面就能很容易的进行配置,并且了解相应的配置如何影响模块的工作流程。
下图是TMS570芯片内部内核和外设的结构框图。点击相应的模块,即可进入该模块的配置页面。
比如我们要配置GPIO A端口,看下面的结构框图和弹出的配置提示,很容易知道DIR、PDR、PSL各个位代表什么含义,配置后端口的工作流程是怎么的。
另外这款软件还支持FreeRTOS,FreeRTOS的配置选项也以图形化的界面呈现出来。
在配置完成后,软件自动生成驱动库的源码、启动文件、链接脚本。然后将可以将这些代码加入到Keil、CCS环境中使用。
下面是生成的I2C驱动文件I2C.c的源码。如果拿这段代码和STM32驱动库对应源码进行比较,TI的驱动库是直接根据界面上的配置生成相应的初始化配置数值,而STM32的则要求传入一个包含初始化参数的结构体,然后根据结构体中的参数进行初始化。无论是在效率还是简洁度上,TI的驱动库都是更加优秀的。
/** @fn void I2CInit(void) * @brief Initializes the I2C Driver * * This function initializes the I2C module. */
void i2cInit(void)
{
/** @b intialize @b I2C1 */
/** - I2C out of reset */
i2cREG1->MDR = (1 << 5); /* I2C reset */
/** - Set I2C mode */
i2cREG1->MDR = /* nack mode */ (0 << 15)
/* free running */ | (0 << 14)
/* start condtion - master mode only */ | (I2C_START_COND)
/* stop condtion */ | (I2C_STOP_COND)
/* Master/Slave mode */ | (1)
/* Transmitter/receiver */ | (I2C_TRANSMITTER)
/* xpanded address */ | (I2C_7BIT_AMODE)
/* repeat mode */ | (0 << 7)
/* digital loopback */ | (0 << 6)
/* start byte - master only */ | (0 << 4)
/* free data format */ | (0)
/* bit count */ | (0x0003);
/** - Set I2C extended mode */
i2cREG1->EMDR = (0 << 25);
/** - Disable all interrupts */
i2cREG1->IMR = 0x7FU;
/** - set prescale */
i2cREG1->PSC = 9;
/** - set clock rate */
i2cREG1->CLKH = 0x1E;
i2cREG1->CLKL = 0x1E;
/** - set i2c pins functional mode */
i2cREG1->FUN = (0 );
/** - set i2c pins default output value */
i2cREG1->DOUT = (0 << 1) /* sda pin */
| (0); /* scl pin */
/** - set i2c pins output direction */
i2cREG1->DIR = (0 << 1) /* sda pin */
| (0); /* scl pin */
/** - set i2c pins open drain enable */
i2cREG1->ODR = (0 << 1) /* sda pin */
| (0); /* scl pin */
/** - set i2c pins pullup/pulldown enable */
i2cREG1->PD = (0 << 1) /* sda pin */
| (0); /* scl pin */
/** - set i2c pins pullup/pulldown select */
i2cREG1->PSL = (1 << 1) /* sda pin */
| (1); /* scl pin */
/** - set interrupt enable */
i2cREG1->IMR = (0 << 6) /* Address as slave interrupt */
| (0 << 5) /* Stop Condition detect interrupt */
| (0 << 4) /* Transmit data ready interrupt */
| (0 << 3) /* Receive data ready interrupt */
| (0 << 2) /* Register Access ready interrupt */
| (0 << 1) /* No Acknowledgement interrupt */
| (0); /* Arbitration Lost interrupt */
i2cREG1->MDR |= I2C_RESET_OUT; /* I2C out of reset */
/** - inialise global transfer variables */
g_i2cTransfer[1].mode = 0 << 8;
g_i2cTransfer[1].length = 0;
}
该驱动库还包含使用Doxygen生成的帮助文档,文档中详细介绍了各个模块、函数的使用和功能描述。相对于PDF文件,Doxygen更便于用户查阅。
下面的例子是从帮助文档中摘下来的,演示了如何产生PWM信号。程序相当简洁,仅仅在main()函数中调用hetInit()函数。硬件相关的初始化参数早已经在HAL Code Generator中配置并生成,用户仅仅需要简单调用,不需要写额外的代码。
/* Include common header file - types, definitions and function declarations for all drivers */
#include "sys_common.h"
/* Include system header file - types, definitions and function declarations for system driver */
#include "system.h"
/* Include HET header file - types, definitions and function declarations for system driver */
#include "het.h"
void main(void)
{
/* Initialize HET driver */
hetInit();
/* Run forever */
while(1);
}
/* Pwm interrupt notification (Not used but must be provided) */
void pwmNotification(unsigned pwm, unsigned notification)
{
return;
}
/* Edge interrupt notification (Not used but must be provided) */
void edgeNotification(unsigned edge)
{
return;
}
现在的芯片种类越来越多,复杂度也越来越高,一款芯片的内部寄存器少说也有上百个吧。由于工作的关系,我接触过很多型号的芯片,越来越觉得如果还抱着那种将某款芯片研究透的话,那将是非常非常累而且也没有必要。芯片生产厂家也考虑到这点,相继推出了驱动库,目的就是简化工程师使用芯片的难度。工程师们也越来越接受这点。目前网上也有不少关于是否使用驱动库的讨论,讨论的的焦点主要集中在驱动库的效率不高问题上,也有些技术人员观念没转过来,想什么都自己搞清楚。我觉得绝大部分情况下,驱动库效率并不是主要的问题,首要的问题应当是能否将该芯片快速用起来并解决问题!效率的提高可以往后推迟,并且往往仅取决于一小部分代码。
比较喜欢TI的这种驱动库使用方式,超级简单、易用,而且人性化。TI这样的大公司,推出的工具确实相当专业。我们这些人能够将主要的精力放在具体应用的实现上,而不是纠缠于硬件细节。
转载至我的博客:http://lishutong.me,欢迎大家访问。
|