Calendar
载入中。。。
Placard
载入中。。。
Category
载入中。。。
Latest Entries
载入中。。。
Latest Comments
载入中。。。
Last Messages
载入中。。。
User Login
载入中。。。
Links
Information
载入中。。。
Search
Other


Welcome to my blog!
  NiosII Step By Step (4)--PIO
 

--本篇由Mr_Don版主撰写
   
俗话说柿子拣软的捏: )。我也抖胆放一篇NiosII Step by Step,给大家讲讲PIO。这只是我个人的理解,如果写得有什么问题或不足,欢迎大家跟帖指正交流。

PIOParallel Input/Output)是SOPCBuilder里面一个最常用的IP之一,而且相对简单,容易上手使用。个人感觉PIO可类比于单片机的GPIO管脚,用于用户自己定义的外设操作。一般来说,我们在SOPC系统中用PIO主要是因为方便,因为FPGA的管脚越来越多,内部逻辑资源越来越多,所以设计时几乎不用担心管脚的数目或者逻辑资源是否不够;相比之下单片机的GPIO可能会让用户感到数目不够,有时不够方便。所以在SOPC系统我们可以同时用很多使用PIO接口的设备。大体来说,这些设备主要分输入和输出两类:

输入类:按钮,自定义键盘等
输出类:LEDLCD定义接口连线等
标准件:如pwm步进电机等

如上图所示,在Nios/NiosII系统中,PIO接口IP可用于输入、输出、以及双向口三种类型。另外,SOPCBuilder中的PIO还支持中断检测。不过中断检测及处理只在其作为输入设备时可用。如果是用PIO连接一个输出设备(例如LED),则系统自动禁止中断。

这里只举一个button_pio4位)作为输入接口的例子,如下图:

可以看到,作为输入接口时,另外两个标签项也可选,先看看第二个标签项(作输出接口时另外两个标签选项是自动禁止的):

Edge Capture Register
下面的三个选项是指捕获输入动作时是检测哪一种,用户可以选择只检测上升沿(Rising Edge),这样PIO内部的Edge Capture Register只在有上升沿动作时接收数据。同样道理,另外的两个选项一看便知。对于Button,我们一般倾向于选双向沿均可监测到数据变化。
下面的Interrupt选项是指用户选择以什么方式作为中断的输入,一种是电平变化产生中断,一种是沿的变化产生中断。第一种方式只有在输入由低电平变化为高电平时才有效(不过用户可以根据需要,在做好的Nios模块外面的相应管脚上加上非门,使之由高电平变电平时生效);第二种方式只有当Edge Capture Register位是1(高电平)时才有用。

再来看一看第三个标签下面的东东:

这个标签下面的东西是用于仿真的。例如如果我们在测试输入设备时想给他一个激励,这里我用的是0x0001。什么意思呢?即表示最低位的button位为高电平1。如果我用的激励数据是0x0003,这又是什么意思呢?聪明的你一定会发现这表示激励使低两位的button位为1,呵呵。

下面我们来分析一个SOPCBuilder生成的HDL文件:
module led_pio (
                 // inputs:
                  address,
                  chipselect,
                  clk,
                  reset_n,
                  write_n,
                  writedata,

                 // outputs:
                  out_port
               );

  output  [  3: 0] out_port;
  input   [  1: 0] address;
  input            chipselect;
  input            clk;
  input            reset_n;
  input            write_n;
  input   [  3: 0] writedata;

  wire             clk_en;
  reg     [  3: 0] data_out;
  wire    [  3: 0] out_port;
  assign clk_en = 1;
  //s1, which is an e_avalon_slave
  always @(posedge clk or negedge reset_n)
    begin
      if (reset_n == 0)
          data_out <= 0;
      else if (chipselect && ~write_n && (address = = 0))
          data_out <= writedata[3 : 0];
end

  assign out_port = data_out;
endmodule

你一定立刻注意到这段描述的输入端口怎么有点似曾相识?没错,avalon总线的要求就是这样!回忆一下在SOPCBuilder下加入用户自定义逻辑的要求是不是必须得有这几个端口?呵呵。上面的代码并不难看懂,大家可以看到led_pio是如何被avalon总线驱动的。

关于PIO的软件编程,NiosIIIDE的样板工程里有很多程序可以参考,这里我就不赘述了,只是还想捎带介绍一下altera_avalon_pio_regs.h和另外一个细节。

这个头文件比较奇怪,它和其它设备的驱动文件独立开来,专门控制PIO设备。所以一定要记住在你对PIO外设进行编程时一定要包含这个头文件,否则会出错。

include <io.h>

#define IOADDR_ALTERA_AVALON_PIO_DATA(base)           __IO_CALC_ADDRESS_NATIVE(base, 0)
#define IORD_ALTERA_AVALON_PIO_DATA(base)           IORD(base, 0) 
#define IOWR_ALTERA_AVALON_PIO_DATA(base, data)    IOWR(base, 0, data)

#define IOADDR_ALTERA_AVALON_PIO_DIRECTION(base)      __IO_CALC_ADDRESS_NATIVE(base, 1)
#define IORD_ALTERA_AVALON_PIO_DIRECTION(base)        IORD(base, 1) 
#define IOWR_ALTERA_AVALON_PIO_DIRECTION(base, data) IOWR(base, 1, data)

#define IOADDR_ALTERA_AVALON_PIO_IRQ_MASK(base)       __IO_CALC_ADDRESS_NATIVE(base, 2)
#define IORD_ALTERA_AVALON_PIO_IRQ_MASK(base)         IORD(base, 2) 
#define IOWR_ALTERA_AVALON_PIO_IRQ_MASK(base, data) IOWR(base, 2, data)

#define IOADDR_ALTERA_AVALON_PIO_EDGE_CAP(base)       __IO_CALC_ADDRESS_NATIVE(base, 3)
#define IORD_ALTERA_AVALON_PIO_EDGE_CAP(base)         IORD(base, 3) 
#define IOWR_ALTERA_AVALON_PIO_EDGE_CAP(base, data) IOWR(base, 3, data)
 
可以看出,这里主要有DATADIRECTIONIRQ_MASKEDGE_CAP几个寄存器或位的宏定义,而你可以从SOPCBuilder生成的文件中找到相对应的硬件定义。这也说明了PIO实际上主要是由以上几个寄存器控制工作。因此,软件中只要对相应的寄存器针对基地址进行操作写入数据就可以了。

再来看看另一个值得注意的细节。我们可以从样板工程里发现类似于这样的语句:

volatile int edge_capture_button;
#ifdef BUTTON_PIO_BASE
void handle_button_interrupts(void* context, alt_u32 id)
{
  volatile int* edge_capture_ptr = (volatile int*) context;
  *edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0);
}

void init_button_pio()
{
  void* edge_capture_ptr = (void*) &edge_capture_button;
  IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_PIO_BASE, 0xf);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0x0);
  alt_irq_register( BUTTON_PIO_IRQ, edge_capture_ptr, handle_button_interrupts ); 
}
#endif

第一个函数是中断处理,这里暂不介绍。第二个函数是button的初始化函数。我们可以看到主要是三个步骤:将IRQ_MASK置为1(因为这个寄存器位在相应的硬件文件里要取and操作),EDGE_CAP则置为0,因为此时并没有输入设备的沿状态发生变化,因此表示按钮尚未按下;第三步则是中断注册,只要按照软件开发手册上说的去做就好了,不用在乎细节也是可以的,只要注册完毕,你的PIO外设就可以接收外部的输入啦,然后Nios会自动跳到相应的处理程序。

[ 阅读全文 | 回复(0) | 引用通告 | 编辑

  Post  by  jearome 发表于 2006-9-6 12:24:00
发表评论:
载入中。。。
载入中。。。