1、引言
在公司新仪器的研制项目中用了ALTERA的EP2C5系列FPGA控制DDS产生波形并控制AD进行数据采集,并采用串口和其他电路通信。在编写测试仿真模拟串口时就想在单片机里那么简单的功能在FPGA里怎么这么麻烦。后来和同事交流得知可以利用NIOS II在FPGA里边嵌入软核使FPGA变成单片机。当时感觉很神奇,于是就想研究研究NIOS II到底是什么东西。在网上浏览了很多NIOS II的帖子后,感觉很有趣决定深入学习一下。于是购买了人气很高的黑金开发板。接下来一有时间就照着《NIOS II那些事》里的例程来学习。在学习的过程中逐渐的感觉到自己的C语言知识还很渣。好多概念还有没掌握。因此决定把这些概念重新学习一下。
2、typedef关键字简介
首先是关键字typedef。typedef是C/C++的一个关键字,主要用途是为数据类型创建新的名字。它不能和auto、extern、static、volatile等其他关键字出现在一个表达式里。一般使用typedef有两个目的,一是给变量定义一个简单易懂且意义明确的新名字,二是简化复杂的类型声明。
3、typedef的一个应用实例
在黑金开发板上学习编写NIOS外设驱动程序时,总是遇到typedef + struct。比如下图所示的I/O驱动程序。这段程序定义了一个名为PIO_STR的结构体,然后定义了LED的宏,(PIO_STR *)PIO_LED_BASE实质是一个结构体变量,这个结构体变量是由指向PIO_LED_BASE的PIO_STR结构体的指针来实现的,指针指向的地址就是PIO_LED_BASE。而PIO_LED_BASE是FPGA中用于驱动LED的IO口的寄存器的基地址。结构体成员和PIO寄存器的内容一一对应。(PIO_STR *)PIO_LED_BASE这个语句就把结构体的起始地址和PIO寄存器的基地址对应起来。如此一来就可以通过操作结构体的成员来对PIO寄存器进行读写。比如拉低驱动LED引脚的电平可以采用下面的语句实现:
图1 PIO_STR结构体定义
图2 PIO寄存器
通过宏定义分别把结构体的基地址设置为不同的寄存器基地址,就可以通过操作结构体来实现对NIOS II寄存器的操作。
扯的有点远了,但是实在是惊叹上述编程方式的巧妙。
在上边结构体的定义中,采用typedef定义了PIO_STR结构体。PIO_STR就相当于结构体的名称。实际应用中可能需要用到很多I/O口,这些I/O的寄存器都是一样的,因此我们只需要把结构体基地址和相应的I/O口寄存器基地址对应起来就可以。比如下边的语句就定义了和RTC连接的三个I/O端口。
上边的例子中通过使用typede定义了新的PIO_STR类型,使代码变的简洁。
4、typedef的其他用法和注意要点
typedef除了以上的用法外还有其他的用法及使用要点。
(1)和#define的区别
typedef看起来和#define很像,其实两者是不一样的。#define仅仅是简单的文本替换,在编译预处理时把宏定义用原始文本替换。而typedef超越了文本替换,是为数据类型重新定义名字,因此不能简单文本替换代替,需要在编译时由编译器来应对解释处理。
(2)平台无关性
typedef另一个重要作用就是定义与平台无关的数据类型。比如可以定义一个ANGLE浮点类型来获取最高的数据精度:
如果平台不支持long double,可以把定义修改如下:
如果连double类型都不支持,可以修改如下:
这样代码在平台间进行移植时,仅需要修改typedef定义就可以了,不需要修改其他代码。好多标准库或者驱动程序就是采用这种方法。而typedef不是简单的文本替代因此比#define用起来更可靠。