//=====================================================================
//TITLE:
// 应用程序中读写TCC7901的寄存器
//AUTHOR:
// norains
//DATE:
// Monday 12-July-2010
//Environment:
// Windows CE 5.0
//=====================================================================
熟悉于WinCE驱动编写的兄弟姐妹,估计对于嵌入式CPU的寄存器读写不会陌生,甚至可以说就像是左手摸右手那般熟悉。
不过熟悉归熟悉,实际工作中,如果死板地按照微软的驱动编写流程,估计会被烦死;即使没有被烦死,也会效率大打折扣。比方说,你需要写一个GPIO驱动,该驱动会根据某些特定情况开断指示灯。当你酣畅淋漓地将驱动写完,兴致勃勃部署到设备上,启动,哑火。这时候,我估计很多朋友都会选择通过在代码中遍布打印消息,以此推断结症所在。如果这时候能将相应的操作放置于应用程序,能通过IDE的断点来调试,是不是一切就会简单很多呢?
如果你使用的是Telechips的TCC7901,在应用程序中操作其寄存器,将会是一件非常简单的事--简单到连通用做法都不必采用。
我们以一个最为简单的例子,设置GPIOD[8]功能。
先查看一下TCC7901的Datasheet:
通过图片中用红色圈起来的部分,我们知道,如果要将SDI0这个PIN作为GPIO功能,那我们需要对PORTCFG4这个寄存器的24~27位写入1,并且该寄存器的地址为0xF005A010。
我们先把文档放一边,先来查看一下TCC7901的BSP代码。找到TCC79x_Physical.h这个文件,输入PORTCFG4,我们可以看到在该头文件中对于该寄存器是如此定义的:
#define HwPORTCFG4 *(volatile unsigned long *)0xF005A010 #define HwPORTCFG4_SCLK0(X) ((X)*Hw28) #define HwPORTCFG4_SDI0(X) ((X)*Hw24) #define HwPORTCFG4_SDO0(X) ((X)*Hw20) #define HwPORTCFG4_GPIOA2(X) ((X)*Hw16) #define HwPORTCFG4_GPIOA3(X) ((X)*Hw12) #define HwPORTCFG4_GPIOA4(X) ((X)*Hw8) #define HwPORTCFG4_GPIOA5(X) ((X)*Hw4) #define HwPORTCFG4_CSN_CS0(X) ((X)*Hw0)
仔细点观察,我们会发现,如果将这些定义的前缀Hw去掉,那么剩下的名字就和Datasheet上的完全一一对应。没错,确实如此。Telechips为了方便,已经将所有寄存器的地址定义在一个头文件中,Datasheet中提到的寄存器名,只要加上Hw前缀,就能在头文件中找到其相应的位置。
不仅如此,Telechips还在该文件定义了寄存器的相应操作:
#ifndef BITSET #define BITSET( X, MASK) ( (X) |= (unsigned int)(MASK) ) #endif #ifndef BITSCLR #define BITSCLR(X, SMASK, CMASK) ( (X) = ((((unsigned int)(X)) | ((unsigned int)(SMASK))) & ~((unsigned int)(CMASK))) ) #endif #ifndef BITCSET #define BITCSET(X, CMASK, SMASK) ( (X) = ((((unsigned int)(X)) & ~((unsigned int)(CMASK))) | ((unsigned int)(SMASK))) ) #endif #ifndef BITCLR #define BITCLR( X, MASK) ( (X) &= ~((unsigned int)(MASK)) ) #endif #ifndef BITXOR #define BITXOR( X, MASK) ( (X) ^= (unsigned int)(MASK) ) #endif #ifndef ISZERO #define ISZERO(X, MASK) ( ! (((unsigned int)(X)) & ((unsigned int)(MASK))) ) #endif #ifndef ISSET #define ISSET(X, MASK) ( (unsigned int)(X) & ((unsigned int)(MASK)) ) #endif #ifndef IS #define IS(X, MASK) ( (unsigned int)(X) & ((unsigned int)(MASK)) ) #endif #ifndef ISONE #define ISONE(X, MASK) ( (unsigned int)(X) & ((unsigned int)(MASK)) ) #endif
以文章开头的例子,如果我们想对PORTCFG4进行设置,那么代码会非常简单:
BITCSET(HwPORTCFG4, HwPORTCFG4_SDI0(0xFUL), HwPORTCFG4(1))
那么,这代码能不能正常运行呢?很遗憾,结论是:不行。因为这个是硬件的地址,如果你应用程序强制往该地址写数据,你得到的将是一个错误。
似乎遇到了一个难题,但解决方法却是非常简单。我们不包含TCC79x_Physical.h文件,而改为TCC79x_Virtual.h。这两个文件宏定义的名称完全相同,没有任何差别,唯一的差异是,TCC79x_Virtual.h中定义的地址是虚拟内存地址--而这个地址完全可以直接在应用程序中使用。
所以,对于文中提出的问题,以代码的形式,则是简单如此:
#include "stdafx.h" #include "TCC79x_Virtual.h" int main(int argc, char** argv) { BITCSET(HwPORTCFG4, HwPORTCFG4_SDI0(0xFUL), HwPORTCFG4(1)); return 0; }
其实,借助于TCC79x_Virtual.h文件,我们能做的事情还很多。比如,该文件还对每一位进行了定义:
#define Hw37 (1LL << 37) #define Hw36 (1LL << 36) #define Hw35 (1LL << 35) #define Hw34 (1LL << 34) #define Hw33 (1LL << 33) #define Hw32 (1LL << 32) #define Hw31 0x80000000 #define Hw30 0x40000000 #define Hw29 0x20000000 #define Hw28 0x10000000 #define Hw27 0x08000000 #define Hw26 0x04000000 #define Hw25 0x02000000 #define Hw24 0x01000000 #define Hw23 0x00800000 #define Hw22 0x00400000 #define Hw21 0x00200000 #define Hw20 0x00100000 #define Hw19 0x00080000 #define Hw18 0x00040000 #define Hw17 0x00020000 #define Hw16 0x00010000 #define Hw15 0x00008000 #define Hw14 0x00004000 #define Hw13 0x00002000 #define Hw12 0x00001000 #define Hw11 0x00000800 #define Hw10 0x00000400 #define Hw9 0x00000200 #define Hw8 0x00000100 #define Hw7 0x00000080 #define Hw6 0x00000040 #define Hw5 0x00000020 #define Hw4 0x00000010 #define Hw3 0x00000008 #define Hw2 0x00000004 #define Hw1 0x00000002 #define Hw0 0x00000001 #define HwZERO 0x00000000
如果我们的程序的流程依赖于PORTCFG4的第2位的话,那么我们的代码流程也可以简单地书写如下:
if(HwPORTCFG4 & Hw1) { ... }
总而言之,对于TCC7901寄存器的读写,完全可以在应用程序中进行,这对于产品调试的便利性,不言而喻。