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


    引子

    定时器中断,我以前在艾米电子论坛发帖讨论过;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.1 例化PIO核

    图1.2 Basic Setting

    图1.2 Basic Setting

    图1.3 Input Option

    图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.4 Capture功用

    图1.5 IRQ Generation功用

    图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, 1); // 清中断边沿捕获寄存器
      // 注册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, 1); // 清中断边沿捕获寄存器
    
      // 用户中断代码
    }
    

    以第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); // 使能中断
    

    同时需要注意若是在SOPC Builder中选择了enable bit-clearing for edge capture register的话,那么对于edge capture就应该是写1清中断;若是没有选择enable bit-clearing for edge capture register,则是写任意数清中断。(由韩彬总结)

    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(ADS_NIRQ_BASE, 1); // 清中断边沿捕获寄存器
    

    若是总线,便与上面类似。

    在清中断这一点上,我们可以总结一下:arm和mips类的nios是写1清中断;而51单片机是写0清中断。

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

    2 定时器中断

    此处以high_res_timer为例。

    2.1 在SOPC Builder中例化Timer

    图2.1 例化Interval Timer核

    图2.1 例化Interval Timer核

    图2.2 配置Timer counter size和Hardware option

    图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.3 Counter Size功用

    图2.4 Hardware Options功用

    图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
    
      // 用户中断代码
    }
    

    image

    图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

     安德鲁® / CC BY 2.5     FPGA Run!
  • 相关阅读:
    Redis 在 分布式项目中 的小应用--分布式锁 模拟session 分布式自增id
    自动生成swagger 前后端分离 对接文档
    利用IDEA 把maven工程中spring-boot的某个模块打成jar包
    利用SQL语句,把本地Excel文件批量导入mysql数据库,做测试数据使用
    Java常用API-String类
    网络编程模型及TCP、UDP编程设计
    计算机网络模型构架分析
    多线程程序的设计详解
    多进程、进程间通讯设计
    Linux 文件编程、时间编程基本函数
  • 原文地址:https://www.cnblogs.com/yuphone/p/1887621.html
Copyright © 2020-2023  润新知