应用程序中读写TCC7901的寄存器
//=====================================================================
//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寄存器的读写,完全可以在应用程序中进行,这对于产品调试的便利性,不言而喻。