zoukankan      html  css  js  c++  java
  • [笔记].怎样使用Nios II中的sys_clk_timer?

    本文简单描述如何使用sys_clk_timer服务,来控制led每100ms开关一次。

    使用环境:Altera Quartus 9.1 SP1 + Nios II  9.1 Software Build Tools for Eclipse SP1

    步骤1 在SOPC Builder中例化Interval Timer核:

    1. 在SOPC Builder中例化Interval Timer核,命名为sys_clk_timer。

    图1 添加Interval Timer核

    图1 例化Interval Timer核

    注意:命名为sys_clk_timer只是为了和BSP中sys_clk_timer一致,也可以命名为其他名称。

    图2 BSP中的相关设置

    图2 BSP中的相关设置

    2. 配置Interval Timer核

    图3 配置Interval Timer核

    图3 配置Interval Timer核

    Timerout period配置为1ms,表示每个tick是1ms;关于tick的概念,后面会提到。Timer counter size默认为32位。Hardware options可以自行配置;因为此处只是用到了sys_clk_timer服务,所以选择默认选项之一Simple periodic interrupt即可,即简单的周期中断。关于Timerout period的概念,手册中的介绍如下,大家可自行阅读。

    图4 手册中的Timerout period的概念

    图4 手册中的Timerout period的概念

    注意:由于Writeable period选项没有用,所以Timerout period是固定的。

    步骤2 NIOS II EDS中编写相应的C程序

    1. 查看system.h中的相应内容

    #define ALT_MODULE_CLASS_sys_clk_timer altera_avalon_timer
    #define SYS_CLK_TIMER_ALWAYS_RUN 1
    #define SYS_CLK_TIMER_BASE 0x1002000
    #define SYS_CLK_TIMER_COUNTER_SIZE 32
    #define SYS_CLK_TIMER_FIXED_PERIOD 1
    #define SYS_CLK_TIMER_FREQ 125000000u
    #define SYS_CLK_TIMER_IRQ 2
    #define SYS_CLK_TIMER_IRQ_INTERRUPT_CONTROLLER_ID 0
    #define SYS_CLK_TIMER_LOAD_VALUE 124999ULL
    #define SYS_CLK_TIMER_MULT 0.0010
    #define SYS_CLK_TIMER_NAME "/dev/sys_clk_timer"
    #define SYS_CLK_TIMER_PERIOD 1
    #define SYS_CLK_TIMER_PERIOD_UNITS "ms"
    #define SYS_CLK_TIMER_RESET_OUTPUT 0
    #define SYS_CLK_TIMER_SNAPSHOT 0
    #define SYS_CLK_TIMER_SPAN 32
    #define SYS_CLK_TIMER_TICKS_PER_SEC 1000u
    #define SYS_CLK_TIMER_TIMEOUT_PULSE_OUTPUT 0
    #define SYS_CLK_TIMER_TYPE "altera_avalon_timer"

    第5行,固定的Timerout period,为1;第13行,是Timerout period的单位。

    第17行,由于每个tick是1ms,因此1s有1000个ticks。

    第6行,Nios II软核的输入时钟频率,此处为125MHz。

    2. 示范程序

    #include "system.h"                     // SOPC Builder配置后的存储映射头文件
    #include "altera_avalon_pio_regs.h"     // PIO核的存储映射头文件
    #include "alt_types.h"                  // Altera的数据类型
    #include "sys/alt_alarm.h"              // Interval Timer核的驱动头文件
    #include "unistd.h"                     // NULL
    
    // 用户回调函数
    alt_u32 timer_CallBackFunc(void* context);
    
    alt_alarm timer_addr;                   // 指向结构体alt_alarm的指针
    alt_u32 ticks_num = 100;                // 100 ticks  * 1 ms/tick = 100 ms
    
    int main()
    {
      /*
       * 函数功能:启动sys_clk_timer服务
       * 函数备注:#include "sys/alt_alarm.h"
       * 入口参数:timer_addr,指向结构体alt_alarm的指针
       *        ticks_num,每隔ticks_num执行一次回调函数
       *        timer_CallBackFunc,用户回调函数
       *        context,传给用户回调函数的参数,此处为NULL
       */
      alt_alarm_start(&timer_addr, ticks_num, timer_CallBackFunc, NULL);
      while(1);
      return 0;
    }
    
    /*
     * 函数功能:用户回调函数
     * 函数备注:用户自己编写
     * 入口参数:*context, 从alt_alarm_start()传来的参数
     * 出口参数:ticks_num, sys_clk_timer服务的周期数
     */
    alt_u32 timer_CallBackFunc(void* context)
    {
      static alt_u8 temp = 0;               // static 定义时,只可赋值一次
      IOWR_ALTERA_AVALON_PIO_DATA(Q_LED_BASE, temp);
      temp = ~temp;                         // 翻转temp
      return ticks_num;                     // 返回下一次sys_clk_timer服务的ticks_num
    }

    对应前面提到的Simple periodic interrupt,34行到40行的用户回调函数,可以看作是定时器中断服务程序;23行的alt_alarm_start()可以看作是定时器中断的注册函数。ticks_num有什么用呢,前面已经提到1个tick是1ms(SOPC Builder中配置的),那么一旦定时器使用alt_alarm_start()运行起来,回调函数就会每ticks_num*1ms/tick执行一次。

    注意:第36行,用户回调函数内,变量的声明必须加上关键字static,以防止被编译器优化以致错误出现。第23行的ticks_num为首次sys_clk_timer服务的ticks_num;第39行的ticks_num为下一次sys_clk_timer服务的ticks_num;两者可以不同;读者可以在第36行用数字替换试试。还有每个tick的时间只与SOPC Builder中的配置有关,和Nios II软核的输入时钟没有太大的关系。  

    至于为什么叫回调函数,我们可以打开sys/alt_alarm.h,查看相关函数的原型。此处由于水平有限,不做相关解析。请读者自行分析。

    /*
     * "alt_alarm" is a structure type used by applications to register an alarm
     * callback function. An instance of this type must be passed as an input
     * argument to alt_alarm_start(). The user is not responsible for initialising
     * the contents of the instance. This is done by alt_alarm_start(). 
     */
    
    typedef struct alt_alarm_s alt_alarm;
    
    /* 
     * alt_alarm_start() can be called by an application/driver in order to register
     * a function for periodic callback at the system clock frequency. Be aware that
     * this callback is likely to occur in interrupt context. 
     */
    
    extern int alt_alarm_start (alt_alarm* the_alarm, 
                                alt_u32    nticks, 
                                alt_u32    (*callback) (void* context),
                                void*      context);
    
    /*
     * alt_alarm_stop() is used to unregister a callback. Alternatively the callback 
     * can return zero to unregister.
     */
    
    extern void alt_alarm_stop (alt_alarm* the_alarm);
    
    /*
     * Obtain the system clock rate in ticks/s. 
     */
    
    static ALT_INLINE alt_u32 ALT_ALWAYS_INLINE alt_ticks_per_second (void)
    {
      return _alt_tick_rate;
    }
    
    /*
     * alt_sysclk_init() is intended to be only used by the system clock driver
     * in order to initialise the value of the clock frequency.
     */
    
    static ALT_INLINE int ALT_ALWAYS_INLINE alt_sysclk_init (alt_u32 nticks)
    {
      if (! _alt_tick_rate)
      {
        _alt_tick_rate = nticks;
        return 0;
      }
      else
      {
        return -1;
      }
    }
    
    /*
     * alt_nticks() returns the elapsed number of system clock ticks since reset.
     */
    
    static ALT_INLINE alt_u32 ALT_ALWAYS_INLINE alt_nticks (void)
    {
      return _alt_nticks;
    }

    参考资料

    1. Volume5: Embedded Peripherals, QuartusII  Handbook Version 9.1

    http://www.altera.com/literature/hb/nios2/n2cpu_nii5v3.pdf

  • 相关阅读:
    返回顶部按钮效果实现
    WebAPI Angularjs 上传文件
    C# 单元测试
    C# 如何获取Url的host以及是否是http
    Dapper批量操作实体
    易优CMS:type的基础用法
    易优CMS:arcview基础用法
    易优CMS:channel的基础用法
    易优CMS:arclist 文档列表
    c语言必背代码
  • 原文地址:https://www.cnblogs.com/yuphone/p/1714554.html
Copyright © 2011-2022 走看看