安德鲁

[笔记].如何使用Nios II的中断:PIO中断与定时器中断.[Nios II]

0
阅读(4096)

引子

定时器中断,我以前在艾米电子论坛发帖讨论过;PIO中断我在博客里也讨论过,最近发现以前的总结有一点小错误。于是结合我最近玩触摸屏的一点点心得,写篇博文。

 

软硬件环境

硬件:艾米电子EP2C8核心板+2.4’ TFT套件

软件:Altera Quartus II 10.0  +  Nios II 10.0  Software Build Tools for Eclipse

 

内容

1 PIO中断

此处以ADS的nIRQ引脚为例。

1.1 在SOPC Builder中例化PIO


图1.1 例化PIO核


图1.2 Basic Setting


图1.3 Input Option

在ADS7843中,当用触摸笔触摸到TFT时,nIRQ引脚会拉低,因此我们可以检测nIRQ引脚的边沿,当为下降沿的时候,产生中断。查看手册Embedded Peripherals IP User Guide中的PIO一节,阅读相关片段。根据图1.4和1.5的描述,对nIRQ的PIO的输入选项的设置如图1.3所示。只需配置图1.2和图1.3所指的选项,其他选项缺省设置即可。


图1.4 Capture功用


图1.5 IRQ Generation功用

1.2 PIO中断的C代码

#include "system.h"                   // 系统
	#include "altera_avalon_pio_regs.h"   // PIO,ads_nIRQ
	#include "sys/alt_irq.h"              // 中断
	//
	unsigned int nirq_isr_context; // 定义全局变量以储存isr_context指针
	void nIRQ_Initial(void);
	void nIRQ_ISR(void* isr_context);
	//
	int main(void)
	{
	  nIRQ_Initial(); // 初始化PIO中断
	  while(1)
	  {
	  } 
	}
	// nIRQ中断初始化
	void nIRQ_Initial(void)
	{
	  // 改写timer_isr_context指针以匹配alt_irq_register()函数原型
	  void* isr_context_ptr = (void*) &nirq_isr_context;
	  IOWR_ALTERA_AVALON_PIO_IRQ_MASK(ADS_NIRQ_BASE, 1); // 使能中断
	  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(ADS_NIRQ_BASE, 0); // 清中断边沿捕获寄存器
	  // 注册ISR
	  alt_ic_isr_register(
	      ADS_NIRQ_IRQ_INTERRUPT_CONTROLLER_ID, // 中断控制器标号,从system.h复制
	      ADS_NIRQ_IRQ,     // 硬件中断号,从system.h复制
	      nIRQ_ISR,         // 中断服务子函数
	      isr_context_ptr,  // 指向与设备驱动实例相关的数据结构体
	      0x0);             // flags,保留未用
	}
	// 中断服务子函数
	void nIRQ_ISR(void* nirq_isr_context)
	{
	  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(ADS_NIRQ_BASE, 0); // 清中断边沿捕获寄存器
	 
	  // 用户中断代码
	}

以第21行为例,使能中断和清清中断边沿捕获寄存器都是按位操作的。此处nIRQ引脚为1位,因此所写的值为0或1。

IOWR_ALTERA_AVALON_PIO_IRQ_MASK(ADS_NIRQ_BASE, 1); // 使能中断

倘若是总线,比方说4位,那么使能的话,就应该如下操作。对其他寄存器的操作也是类似的。

IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUS_4_BASE, 0xF); // 使能中断

注意:
1 中断服务代码区不要使用printf(),否则会严重阻塞中断。
2 如果需要中断服务子函数传参,那么参数必须转为空类型。且所传参数为16位的全局变量。
3 9.1版本的以后的中断注册写法有两种,此处示范为增强版的中断注册写法。

 

2 定时器中断

此处以high_res_timer为例。

2.1 在SOPC Builder中例化Timer


图2.1 例化Interval Timer核


图2.2 配置Timer counter size和Hardware option

查看手册Embedded Peripherals IP User Guide中的Interval Timer核一节,阅读相关片段。参考图2.3和2.4的描述,配置为32位的全功能定时器(当然也可以配置为64位定时器,但是后面的软件需要稍微修改)。只需配置图2.2,其他选项缺省设置即可。


图2.3 Counter Size功用


图2.4 Hardware Options功用

2.2 定时器中断的C代码

#include "system.h"                   // 系统
	#include "altera_avalon_timer_regs.h" // 定时器
	#include "sys/alt_irq.h"              // 中断
	//
	unsigned int timer_isr_context; // 定义全局变量以储存isr_context指针
	void Timer_Initial(void);
	void Timer_ISR(void* isr_context);
	//
	int main(void)
	{
	  Timer_Initial(); // 初始化定时器中断
	  while(1)
	  {
	  } 
	}
	// 定时器中断初始化
	void Timer_Initial(void)
	{
	  // 改写timer_isr_context指针以匹配alt_irq_register()函数原型
	  void* isr_context_ptr = (void*) &timer_isr_context;
	  // 设置PERIOD寄存器
	  // PERIODH << 16 | PERIODL = 计数器周期因子 * 系统时钟频率因子 - 1
	  // PERIODH << 16 | PERIODL = 5m*100M - 1 = 499999 = 0x7A11F
	  IOWR_ALTERA_AVALON_TIMER_PERIODH(HIGH_RES_TIMER_BASE, 0x0007);
	  IOWR_ALTERA_AVALON_TIMER_PERIODL(HIGH_RES_TIMER_BASE, 0xA11F);
	 
	  // 设置CONTROL寄存器
	  //    位数 |  3   |  2   |  1   |  0  |
	  // CONTROL | STOP | START| CONT | ITO |
	  // ITO   1,产生IRO;                      0,不产生IRQ
	  // CONT  1,计数器连续运行直到STOP被置一;   0,计数到0停止
	  // START 1,计数器开始运行;                0,无影响
	  // STOP  1,计数器停止运行;                0,无影响
	  IOWR_ALTERA_AVALON_TIMER_CONTROL(HIGH_RES_TIMER_BASE,
	    ALTERA_AVALON_TIMER_CONTROL_START_MSK | // START = 1
	    ALTERA_AVALON_TIMER_CONTROL_CONT_MSK  | // CONT  = 1
	    ALTERA_AVALON_TIMER_CONTROL_ITO_MSK);   // ITO   = 1
	  // 注册定时器中断
	  alt_ic_isr_register(
	      HIGH_RES_TIMER_IRQ_INTERRUPT_CONTROLLER_ID, // 中断控制器标号,从system.h复制
	      HIGH_RES_TIMER_IRQ,     // 硬件中断号,从system.h复制
	      Timer_ISR,              // 中断服务子函数
	      isr_context_ptr,        // 指向与设备驱动实例相关的数据结构体
	      0x0);                   // flags,保留未用
	}
	// 定时器中断服务子函数
	void Timer_ISR(void* timer_isr_context)
	{
	  // 应答中断,将STATUS寄存器清零
	  IOWR_ALTERA_AVALON_TIMER_STATUS(HIGH_RES_TIMER_BASE,
	      ~ ALTERA_AVALON_TIMER_STATUS_TO_MSK);   // TO = 0
	 
	  // 用户中断代码
	}

图2.3 32位的Interval Timer核的寄存器结构。

查看手册Embedded Peripherals IP User Guide中的Interval Timer核一节,阅读相关片段。参考图2.3,设置寄存器。此处以5ms为例,系统时钟为100Mhz。之所以还要减去1,是因为从0开始计数的。关于其他寄存器的功用,请参阅手册的相关片段。

// 设置PERIOD寄存器
	// PERIODH << 16 | PERIODL = 计数器周期因子 * 系统时钟频率因子 - 1
	// PERIODH << 16 | PERIODL = 5m*100M - 1 = 499999 = 0x7A11F
	IOWR_ALTERA_AVALON_TIMER_PERIODH(HIGH_RES_TIMER_BASE, 0x0007);
	IOWR_ALTERA_AVALON_TIMER_PERIODL(HIGH_RES_TIMER_BASE, 0xA11F);

与PIO注意事项类似。
注意:
1 中断服务代码区不要使用printf(),否则会严重阻塞中断。
2 如果需要中断服务子函数传参,那么参数必须转为空类型。且所传参数为16位的全局变量。
3 9.1版本的以后的中断注册写法有两种,此处示范为增强版的中断注册写法。

3 一点心得

Nios II的中断操作基本类似。今后我会写点其他IP和自定义IP里面的中断如何使用。

Embedded Peripherals IP User Guide提供了很多IP核的功能和用法说明,是学习Nios II的红宝书。关于Nios II软件编程的更多细节,可以参阅Nios II Software Developer's Handbook;关于Nios II软核的更多细节,可以参阅Nios II Processor Reference Handbook。如果你有一些解决不了的问题,可以去alterafoum.com检索相关关键字,很多时候是可以找到前人的经验和答案的。

 

另见

[笔记].Nios II 9.1的sys/alt_irq.h与之前版本的区别.[Nios II]

 

参考

Altera.Embedded Peripherals IP User Guide