技术天地

基于SPI接口的大容量通用数据采集方案    发布时间:2016-12-20    被阅览数:

  数据采集是工业控制系统中的重要环节,较高的采样率对数据处理环节提出了高的要求。当数据量不大,采样率不高时,使用CPU进行传输处理是非常简单方便的;当遇到大的数据容量,高的采样率时,如果仍然使用CPU处理数据传输,将会带来巨大的CPU负载,难以满足高速大容量数据采集的要求。通常,在数据容量比较大,采样率较高的场合,使用DMA技术将数据直接传输到内存,不经过CPU管理,是比较通用的方案。


  英创公司针对英创主板ESM335x已有的硬件资源,在linux-4.1.6操作系统环境下,提出了一种基于SPI接口的大容量通用数据采集方案,其物理连接如图1所示。这里用另一块ESM335x作为主设备,模拟数采装置,实际使用可以是任何支持SPI主模式的设备。使用时,连接SPI主从设备的公共地后,只需要连接ESM335x主板上对应SPI_SCLK、SPI_MOSI、SPI_CS0N的 3个管脚,见表1。


基于SPI接口的大容量通用数据采集方案.gif

图1 SPI接口大容量通用数据采集连接图


  表1 ESM335x工控主板SPI接口数采方案管脚说明



信号名称CN2(管脚标号)说明
 GPIO29/SPI_MOSI F14 SPI数据信号,主设备输出,从设备输入
 GPIO30/SPI_SCLK F15 SPI时钟信号,主设备输出,从设备输入
 GPIO31/SPI_CS0N F16 SPI片选信号,低有效,主设备输出,从设备输入


  该方案使用SPI作为传输协议,采用双buffer的DMA技术,能够达到1Msps(一个采样点数据位宽8-16位)。ESM335x工作在SPI从模式,能够接收的最高时钟为16MHz(最低不限制),即最高数据传输率为2MBytes/s。当DMA缓存buffer1装满数据后,会触发DMA中断,通知CPU将数据读出DMA缓存,然后继续将新传输进入的数据存储在buffer2;buffer2装满数据后,也产生DMA中断通知CPU取出数据,然后将新数据存储到buffer1,如此循环,如图2所示。当主机传输完成不再提供时钟信号后,ESM335x(从设备)通过定时器超时读出DMA缓存中剩余的数据。


基于SPI接口的大容量通用数据采集方案.gif

图2 DMA双buffer示意图


基于SPI接口的大容量通用数据采集方案.gif

图3 使用DMA技术的SPI数据采集CPU负载


  如图3所示,使用此方案后,CPU负载率很低,此例中不到1%。用户使用时,需要按如下步骤进行操作:


  1、加载SPI从模式驱动。在linux操作系统中,使用insmod spi-slave.ko命令,会创建设备节点/dev/spi-slave。


基于SPI接口的大容量通用数据采集方案.gif


  2、应用程序打开设备:

  fd = open ( "/dev/spi-slave", O_RDWR, S_IRUSR | S_IWUSR );


  3、设定传输参数:

  //configure info transfer to driver

  struct spi_slave_transfer

  {

      unsigned int clk;               //驱动根据不同clk,设定不同长度的dma buffer,满足填满一个buffer的时间不超过10ms(双buffer

      unsigned int mode;              //SPI mode: 0,1,2,3

      unsigned int bits_per_word;     //每个采样点的位数

  };

  struct spi_slave_transfer transfer;

  transfer.clk =16000000; //16M clk ---16KB every buffer

  transfer.mode = 1;

  transfer.bits_per_word = 16;


  4、传入参数至内核,启动传输:

  if(ioctl ( fd, SPI_SLAVE_START, &transfer )<0)

  {

      printf ( "START WRONG!!!!!!!!!!!!!!!!\n" );

      exit ( 1 );

  }


  此时,主板上的SPI已经进入从模式,有数据传入时,将存入DMA缓存,存满一个buffer就通知CPU读出数据到CPU维护的一个内存区域(256个kfifo组成链表,kfifo大小与buffer相同,使用完后会覆盖第一个kfifo)。同时,当一次传输完成后,通过定时器读出剩余在DMA buffer中的数据。应用程序应及时使用read函数从CPU维护的区域读出数据,以免CPU维护太多内存。


  count_in_byte = 0;

    read_count = 0;

    while(1)

    {

        FD_ZERO(&fdRead);

        FD_SET(fd,&fdRead);

 

        aTime.tv_sec = 2;

        aTime.tv_usec = 0;

        ret = select ( fd+1, &fdRead, NULL, NULL, &aTime );

        if ( ret<0 )

            printf( "select, something wrong!\n " );

        if ( ret>0 )

        {

            if ( FD_ISSET(fd, &fdRead) )

            {

                memset(read_buf,0,4096*4);

                read_count = read(fd, read_buf, 4096*4);

                if ( read_count<0 )

                {

                    printf ( "READ WRONG!!!!!!!!!!!!!!!!\n" );

                    exit ( 1 );

                }

 

                if(read_count){ //0 --- end-of-file not printf

                    count_in_byte += read_count;

                    printf("\nread_count = %d\ncount_in_byte = %d\n", read_count, count_in_byte);

                }

                //process data, here just print to console

                if(read_count < 20){

                    for ( i=0; i<read_count; i++ )

                    {

                        printf ( "%02x ", read_buf[i] );

                        if (i%10 == 9)

                            printf ( "\n" );

                    }

                    printf("\n");

                }

            }

        }

        printf ( "remaining time %u.%u!\n",aTime.tv_sec, aTime.tv_usec );

    }


  5、完成传输,关闭SPI。

  if(ioctl ( fd, SPI_SLAVE_STOP, &transfer )<0)

    {

        printf ( "STOP WRONG!!!!!!!!!!!!!!!!\n" );

        exit ( 1 );

    }


  6、关闭设备文件

  close ( fd );


  当主设备前后两次传输的参数不一样时,从设备需要分两次调用open/close函数,按以上步骤进行操作。如有用户对这个方案感兴趣,可以联系我们,我们将提供驱动文件和完整的应用程序示例。

Go Top