【Altera SoC体验之旅】高速数据采集之数据传输(2)
作者:chenzhufly QQ:36886052
1、 硬件环境
硬件平台: Embest SoC --LarkBoard
软件平台:开发板-linux-3.10.31
Quartus 14.0
2、系统概述
在上一篇文章已经成功进行了这样的测试,每中断一次,ARM从FPGA的ROM中取一次数据,这边文章主要是测试一下,ADC的数据经过双口RAM传递给ARM的过程,设计框图如下所示:
3、Qsys设计
下图是qsys的设计和连接关系如下图所示:
1) 如图所示adc_origin_data挂载在HPS-to-FPGA bridge上,偏移地址为20040000(这个地址是可以任意改动的,注意和驱动对应就行了)
2) 增加了adc_spi_pio的控制接口,实现对ADC的配置和初始化,这个前面的测试中是没有注意的
4、ADC驱动设计
这里测试驱动主要使用了Embest自带的驱动,位于drivers\char\adc9628.c,具体代码可见附件:
这是一个很好的参考用例,实际使用的时候还需要进一步的修改。上面qsys中偏移地址的设置就是为了和驱动相对应的。
主要完成以下功能:
1)通过模拟SPI接口完成ADC9628的初始化
- /*
- *init the registers of ADC9628,
- *write to Altera's PIO IP core to operate the gpio as spi pins
- */
- static void bsp_adc_init(struct adc_priv_data *adc_data)
- {
- int iadc_val ;
-
-
- writew( 0x7,adc_data->regbase + (0x1*4)); /* pio_dir = 0x7 */
- writew( 0x6,adc_data->regbase + (0x4*4)); /* cs = 1 */
- writew( 0x2,adc_data->regbase + (0x4*4)); /* sclk=0 */
- mdelay(2);
-
- iadc_val = ad9628_read(0x1 ); /* chip ID, 0x89 */
-
- printk(KERN_INFO"current id = %x \n", iadc_val);
-
- iadc_val = ad9628_read(0x2 ); /* chip grade */
- iadc_val = ad9628_read(0x0b ); /* clock divide */
- iadc_val = 0x0;
- ad9628_write(0x0b, iadc_val);
- iadc_val = ad9628_read(0x0b );
- iadc_val = ad9628_read(0x09 ); /* DUTY stabilty */
- iadc_val = ad9628_read(0x0b ); /* clock ratio */
- ad9628_write(0x05, 0x03); /* Channel a b */
- iadc_val = ad9628_read(0x5 );
- ad9628_write(0x0d, 0x0); /* normal mode */
-
- iadc_val = ad9628_read(0xd );
- iadc_val = ad9628_read(0x101 );
-
- iadc_val |= (0x1<<7);
- ad9628_write(0x101, iadc_val);
- iadc_val = ad9628_read(0x101 );
-
- bsp_adc_cmos();
- iadc_val = ad9628_read(0x14 );
-
- }
2)完成读双口RAM的操作
- static const struct file_operations adc_fops = {
- .read = adc_read,
- .open = adc_open,
- .release = adc_release,
- .owner = THIS_MODULE,
- };
3)把ADC注册成一个char型设备
- static int __init adc9628_init(void)
- {
- dev_t dev = MKDEV(LARK_ADC_MAJOR, 0);
- int ret;
-
- ret = register_chrdev_region(dev, max_adc_minors, "adc");
- if (ret)
- goto error;
-
- cdev_init(&adc_cdev, &adc_fops);
- ret = cdev_add(&adc_cdev, dev, max_adc_minors);
- if (ret) {
- goto error_region;
- }
-
- adc_class = class_create(THIS_MODULE, "adc");
- if (IS_ERR(adc_class)) {
- printk(KERN_ERR "Error creating adc class.\n");
- cdev_del(&adc_cdev);
- ret = PTR_ERR(adc_class);
- goto error_region;
- }
- adc_class->devnode = adc_devnode;
- device_create(adc_class, NULL, MKDEV(LARK_ADC_MAJOR, 0), NULL, "adc");
-
- if (!request_mem_region(BSP_ADC_ADDR,sizeof(u32),"adc9628")) {
- printk( "Memory region busy\n");
- return -EBUSY;
-
- }
- gRegbase = ioremap_nocache(BSP_ADC_ADDR, sizeof(u32));
- if(!gRegbase) {
- printk( KERN_INFO" ioremap failed\n");
- return -EIO;
- }
-
- if (!request_mem_region(BSP_FIFO_BASE,sizeof(u32)*1024,"adc9628_fifo")) {
- printk( KERN_INFO"Memory region busy\n");
- return -EBUSY;
-
- }
- gFifobase = ioremap_nocache(BSP_FIFO_BASE, sizeof(u32)*1024);
- if(!gFifobase) {
- printk( KERN_INFO" ioremap failed\n");
- return -EIO;
- }
-
- return 0;
-
- error_region:
- unregister_chrdev_region(dev, max_adc_minors);
- error:
- return ret;
- }
至此ADC驱动介绍完毕,记得在编译系统的时候把驱动编译进去就行了
5、应用程序设计
这里使用的也是Embest提供的测试程序,详细程序可见附件:
测试程序主要完成了FFT计算,我也没有深入的研究,因为并不是我想要的东西,只是用它做个测试吧,有兴趣的自行研究吧,这里就不分析代码了;
6、测试结果
1) 环境搭建
把信号发生器的两个输出口,分别接ADC9628 的两个输入端,如下图所示:
2)设置信号发生器
3)运行测试程序
- root@arm:~/adc# ./adc_test
- current id = 89
- dong fft
- caculate real valule
-
- 146:chanel0 frequency = 14.970703, amplitude=2185
-
- 293:chanel1 frequency = 30.043945, amplitude=1806
从测试结果看chanel0的频率测量是14.970703Mhz,chanel1的频率测量是30.043945Mhz,和实际设置值相似,且可以按照设定频率进行准确的跟踪,说明数据传输应该没有问题
7、小结
1) 本次主要测试ADC的采集数据通过双口RAM传递给ARM的过程,测试结果一切正常,为下一步的工作打下了基础;
2)HPS-to-FPGA bridge接口传输的性能还有待进一步的测试,目前优先完成系统设计;
3)下一步准备把前面设计的中断,加入到驱动中去,这样驱动就更加实用了;
4)同时准备移植web服务器,把采集的数据能够在web上进行展示,这方面不擅长,有大侠可帮忙吗?非常感谢!
本帖最后由 chenzhufly 于 2015-4-23 00:20 编辑