工控网首页
>

应用设计

>

ESM335x扩展总线中断智能块读写

ESM335x扩展总线中断智能块读写

1、综述

ESM335x 嵌入式主板的提供带中断的精简ISA扩展总线,主要用于支持高速数据采集、多路串口扩展以及其他的高级扩展应用。在这些应用中,往往出现需要按一定的顺序读写一定量的数据的情况,如果用户在应用程序中一次一次的调用读写操作将会很慢而且会占用较多系统资源。我们在Linux-4.1.6 ISA总线驱动增加了中断时数据块读写操作的功能,当有中断发生时,驱动程序会按照用户的设置读写块数据,并将读到的数据进行缓存,读写完之后驱动程序会通知应用程序,用户在应用程序中通过读操作就能获得需要读取的块数据。使用驱动程序在中断时自动读写能简化用户应用程序,减少块读写操作的延迟。具体的使用方法在下面介绍。

2、数据结构及使用说明

使用发生中断时驱动自动读写数据块功能需要在应用程序中使用em335x_drivers.h头文件,使能功能需要使用struct isa_transfer结构体,传入此结构体数组给驱动以设置读写操作的参数:

struct isa_transfer

  {

     const void  *tx_buf;    /* 写数据地址,不为NULL有效 */

      void        *rx_buf;    /* 读数据地址,不为NULL有效 */

      unsigned    len;        /* 读取长度 */

      unsigned    offset;   /* 总线地址 0x00 .. 0xFF */

      unsigned    inc;        /* 每次读写之后地址的增量 */

  };

  ● tx_buf: 不为NULL时, 表示写操作

  ● rx_buf: 不为NULL时,表示读操作,数据读取存放地址

  ● tx_buf和rx_buf不能同时有效

  ● len:读写长度,以字节byte为单位,进行块读写操作时必须为偶数,也可以进行单独的1个byte的数据读写,即设置为1

  ● offset:读写操作地址,块数据读写时offset必须为偶数

  ● inc:每次读写操作后地址增加量,1或者0

总线块数据读写说明

ESM335x精简ISA总线只有8位数据/地址线,但是硬件内部可以进行16bit的读写,并且会自动将16bit的数据分为低8位和高8位两次读写,低8位和高8位处在连续的地址处(即必须有两个相邻的地址),我们在驱动中为了加快读写操作,在用户进行块读写(结构体中的len为偶数)时会使用16bit读写的方式,具体读写方式如下,请用户注意读写地址的变化:

● 设置rx_buf有效,len=4, offset=0, inc=0时,驱动读写地址及顺序为:

    低8位—offset,高8位—offset+1;低8位—offset,高8位—offset+1。

● 设置rx_buf有效,len=4, offset=0, inc=1时,驱动读写地址及顺序为:

    低8位—offset,高8位—offset+1;低8位—offset+2,高8位—offset+3。

写操作相同。

3、应用程序示例

使用ISA总线需要先打开相应的设备文件:

  int fd;

   fd = open("/dev/em335x_isa", O_RDWR);

   printf("open file = %d\n", fd);

   if(fd < 0)

   {

       return fd;    

   }

应用程序需要先初始化要传递给驱动的struct isa_transfer数组,读写顺序和数组中的顺序相同,用户可自行设定,要使用中断自动读写功能数组第一个元素必须初始化为0,否则将直接进行块读写操作,而不使用中断:

#define ARRAY_SIZE(a) (sizeof(a)) //用于计算数组字节数

      uint8_t tx[2] = { 0x5a, 0x55};  //第一次写入数据

      uint8_t tx2[2] = { 0xaa, 0x1b}; //第二次写入数据

      uint8_t rx[8];          //读取数据存放处

      struct isa_transfer tr[4];

     

    //第一个元素必须设置为0才会使能中断自动读写功能

        memset ( &tr[0], 0, sizeof(struct isa_transfer) );

     //第一次操作设置为写

        tr[1].tx_buf = tx;

        tr[1].rx_buf = NULL;

        tr[1].len = ARRAY_SIZE(tx);

          tr[1].offset = 0;

          tr[1].inc = 1;

    //第二次操作设置为读,应用程序中读操作存放数据地址rx,总线起始地址为offset

         tr[2].tx_buf = NULL;

          tr[2].rx_buf = rx;

          tr[2].len = ARRAY_SIZE(rx) ;

          tr[2].offset = 4;

          tr[2].inc = 0;

    //第三次操作设置为写

         tr[3].tx_buf = tx2;

          tr[3].rx_buf = NULL;

          tr[3].len = ARRAY_SIZE(tx2);

          tr[3].offset = 0;

          tr[3].inc = 1;

  调用ioctl函数传递数组地址,使能中断块读写操作:

  ret = ioctl(fd, ISA_IOC_MESSAGE(4), tr );

  设置过后的读取地址必须保证在下次调用ioctl重新设置之前一直有效,用户之后调用read函数时驱动程序会自动将数据写入设置的读取地址(示例中的rx数组),而与用户在read函数中输入的地址无关,这样能够简化用户应用程序中的设置,用户只需要在每次read函数之后到ioctl传递的结构体数组中的读地址处(rx数组)获得数据,并在下次读操作之前进行拷贝或者其他操作以防止数据丢失。

  停止中断块读写操作,只传入一个全为0的struct isa_transfer机构体:

  ret = ioctl(fd, ISA_IOC_MESSAGE(1), &tr[0] );

  read函数读取数据只需以sizeof(struct isa_transfer)为count参数,地址为无关参数,但是建议用户传入之前ioctl处的结构体数组地址,如下所示,之后数据便会读到之前设置的结构体数组中指定的读取数据存入地址处,即rx数组中:

  nNum = read(fd, tr, sizeof(struct isa_transfer));

  需要重新设置读写数据参数时需要重新设置结构体数组,并重新调用ioctl函数。

  设置完成后使用select函数查询ISA总线状态,如有中断发生,并且驱动读写完了设置的数据块,select函数将会返回大于0的数值,并且设置相应的读文件标志,应用程序就可以调用read读取数据了,select函数用法示例:

  //线程函数中调用select函数查询总线状态,如可读,则调用read读取数据到之前设置的地址

  int ISASelectThreadFunc(void* lparam)

  {

      int fd = * (int*)lparam;

      printf ( "fd %d\n", fd );

      fd_set fdRead;

      struct timeval aTime;

      int ret;

      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) )

              {

                  printf( "There is a IRQ!!! AND RW complete!\n" );

      /********************在此调用read************************/

                  read(fd, &tx[0], sizeof(struct isa_transfer));

                  //数据已经读到rx数组中了,用户可在此进行数据处理

              }

          }

          //判断程序运行状态,跳出循环

          //break;

      }

      pthread_exit( NULL );

      return 0;

  }

  //创建线程

  int StartPulseThread( int *fd  )

  {

      pthread_attr_t      attr;

      pthread_t           m_thread;

      int                 res;

 

      res = pthread_attr_init(&attr);

      if( res!=0 )

      {

          printf("Create attribute failed\n" );

      }

     res = pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );

      res += pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

      if( res!=0 )

      {

          printf( "Setting attribute failed\n" );

      }

      res = pthread_create( &m_thread, &attr, (void *(*)  (void*))&ISASelectThreadFunc, fd );

      if( res!=0 )

      {

          return -1;

      }

      pthread_attr_destroy( &attr );

     return 0;

  }

  关于ISA精简总线的读写时序请参考《ESMARC 335x系列工控主板数据手册》,单次读写周期190ns,使用中断发生驱动程序自动块读写功能时,最高写速率为5MB/s,最高读速率受硬件限制为3MB/s(两次读操作之间会有总线仲裁时间间隔),读写时序均与《ESMRC 335x 工控主板数据手册》中的时序相同。我们将在后续工作中使用dma方式加快读速度。

  如有疑问或需要使用此功能,请和我们联系。

投诉建议

提交

查看更多评论
其他资讯

查看更多

Windows下STM32单片机eclipse编译环境搭建

Android Studio应用开发简介

WinCE工控主板WiFi解决方案

敬请关注成都英创微信公众号

WinCE工控主板通过PPI协议连接西门子PLC