概述
该代码实现了一个简单的 ADC 数据采集功能,通过硬件寄存器操作控制 LTC2308 ADC 芯片,采集其指定通道的模拟信号并打印转换结果。
代码框架
如下:
main.c 先是读取DE10-Standard开发板的开关值,将这个值作为通道选择信号输入给LTC2308控制器(adc_ltc2308_fifo模块)。然后main.c 给出一个10次的测量次数并将这个数据写入到LTC2308控制器的寄存器当中。接着main.c 给出了一个触发一轮测量的信号(measure_fifo_start)给LTC2308控制器, 开始一轮的测量。(adc_ltc2308_fifo模块调用adc_ltc2308模块 完成10次测量,并把每次测量的数据缓存到adc_data_fifo模块。)main.c 等待10次测量完成以后,开始读取LTC2308控制器的FIFO中的数据。 一次读一个数据,10次读完延迟200ms,又开始新的一轮测量。
NIOS II 处理器的硬件抽象层(HAL)
HAL提供了一些宏定义来访问外设的寄存器,关于HAL的具体描述请参考文章:
静态地址对齐
本案例的Nios 程序使用静态地址对齐(每个寄存器在Avalon总线上占4个字节的地址),在软件编程时
可以使用IOWR(基地址,寄存器编号(n),数据)对自定义IP的第n个寄存器进行写入操作;
可以使用IORD(基地址,寄存器编号(n)) 对自定义IP的第n个寄存器进行读出操作。
如果使用动态地址对齐,(每个寄存器在Avalon总线上占 数据位宽/8个字节的地址)
在软件编程时,使用IOWR_32DIRECT(数据位宽为32位)、IOWR_16DIRECT(数据位宽为16位)、IOWR_8DIRECT(数据位宽为8位)进行写操作
在软件编程时,使用IORD_32DIRECT(数据位宽为32位)、IORD_16DIRECT(数据位宽为16位)、IOWR_RDIRECT(数据位宽为8位)进行写操作
IOWR_8DIRECT(基地址、地址偏移量、数据),地址偏移量 = 寄存器编号*1
IOWR_16DIRECT(基地址、地址偏移量、数据),地址偏移量 = 寄存器编号*2
IOWR_32DIRECT(基地址、地址偏移量、数据),地址偏移量 = 寄存器编号*4
IORD_8DIRECT(基地址、地址偏移量),地址偏移量 = 寄存器编号*1
IORD_16DIRECT(基地址、地址偏移量),地址偏移量 = 寄存器编号*2
IORD_32DIRECT(基地址、地址偏移量),地址偏移量 = 寄存器编号*4
头文件引入
#include <stdio.h> // 标准输入输出库(用于printf) #include <io.h> // 硬件寄存器操作库(IORD/IOWR) #include <unistd.h> // 提供usleep函数(微秒级延时) #include "system.h" // 自定义系统头文件(定义硬件地址)
在底层硬件里面,adc_ltc2308模块和sw模块的地址定义如下:
在软件BSP生成的时候会根据DE10_Standard_QSYS.sopcinfo文件生成system.h文件,其中就包含硬件寄存器的基地址定义:
代码详解
1. 主函数
void main(void){ int ch = 0; const int nReadNum = 10; // max 1024 int i, Value, nIndex=0;
变量说明:
ch:当前选择的 ADC 通道(0-7)。
nReadNum:每次采样的次数。
Value:存储每次 ADC 转换的结果。
nIndex:循环次数索引,用于打印调试信息。
2. 主循环
while(1){ ...... printf("======================= %d, ch=%d\r\n", nIndex++, ch); ...... } // while
程序将无限循环,每循环一次,nIndex自增1,这个变量是标识当前循环了多少次。
3. 读取开关值
ch = IORD(SW_BASE, 0x00) & 0x07;//从开关寄存器读取输入值
& 0x07操作是取低3位,确保通道号在 0-7 范围内。寄存器的编号0x00、0x01的定义可以参考adc_ltc2308_fifo.v模块的代码。
4. 配置采样次数
// set measure number for ADC convert IOWR(ADC_LTC2308_BASE, 0x01, nReadNum);
向 ADC 控制寄存器写入采样次数。
5. 启动ADC转换
// start measure IOWR(ADC_LTC2308_BASE, 0x00, (ch << 1) | 0x00); IOWR(ADC_LTC2308_BASE, 0x00, (ch << 1) | 0x01); IOWR(ADC_LTC2308_BASE, 0x00, (ch << 1) | 0x00); usleep(1);
第一次写入:清除启动位。
第二次写入:设置启动位,开始转换。
第三次写入:清除启动位,准备下一次转换。
usleep(1):短暂延时,确保信号稳定。
6. 等待转换完成
// wait measure done while ((IORD(ADC_LTC2308_BASE,0x00) & 0x01) == 0x00);
IORD(ADC_LTC2308_BASE, 0x00)是读取 ADC 状态寄存器,寄存器的最低位是10次采集完成标志位。
& 0x01:检查最低位是否为1。
== 0x00:结果为0则一直等待。
7. 读取并打印ADC数据
// read adc value for(i=0;i<nReadNum;i++){ Value = IORD(ADC_LTC2308_BASE, 0x01); printf("CH%d=%.3fV (0x%04x)\r\n", ch, (float)Value/1000.0, Value); }
IORD(ADC_LTC2308_BASE, 0x01):读取 ADC 数据寄存器。
(float)Value / 1000.0:将 ADC 原始值转换为电压值。
LTC2308的单极性状态下可以输入0~4.096V的范围,4.096v*value/212=value/1000。
结果
最终程序运行起来的打印结果如下:
往期推荐阅读: