[笔记].怎样使用Nios II中的sys_clk_timer?.[Nios II][SOPC Builder]
0赞本文简单描述如何使用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核
注意:命名为sys_clk_timer只是为了和BSP中sys_clk_timer一致,也可以命名为其他名称。
图2 BSP中的相关设置
2. 配置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的概念
注意:由于Writeable period选项没有用,所以Timerout period是固定的。
步 骤2 NIOS II EDS中编写相应的C程序
1. 查看system.h中的相应内容
01 |
#define ALT_MODULE_CLASS_sys_clk_timer altera_avalon_timer |
02 |
#define SYS_CLK_TIMER_ALWAYS_RUN 1 |
03 |
#define SYS_CLK_TIMER_BASE 0x1002000 |
04 |
#define SYS_CLK_TIMER_COUNTER_SIZE 32 |
05 |
#define SYS_CLK_TIMER_FIXED_PERIOD 1 |
06 |
#define SYS_CLK_TIMER_FREQ 125000000u |
07 |
#define SYS_CLK_TIMER_IRQ 2 |
08 |
#define SYS_CLK_TIMER_IRQ_INTERRUPT_CONTROLLER_ID 0 |
09 |
#define SYS_CLK_TIMER_LOAD_VALUE 124999ULL |
10 |
#define SYS_CLK_TIMER_MULT 0.0010 |
11 |
#define SYS_CLK_TIMER_NAME "/dev/sys_clk_timer" |
12 |
#define SYS_CLK_TIMER_PERIOD 1 |
13 |
#define SYS_CLK_TIMER_PERIOD_UNITS "ms" |
14 |
#define SYS_CLK_TIMER_RESET_OUTPUT 0 |
15 |
#define SYS_CLK_TIMER_SNAPSHOT 0 |
16 |
#define SYS_CLK_TIMER_SPAN 32 |
17 |
#define SYS_CLK_TIMER_TICKS_PER_SEC 1000u |
18 |
#define SYS_CLK_TIMER_TIMEOUT_PULSE_OUTPUT 0 |
19 |
#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. 示范程序
01 |
#include "system.h" // SOPC Builder配置后的存储映射头文件 |
02 |
#include "altera_avalon_pio_regs.h" // PIO核的存储映射头文件 |
03 |
#include "alt_types.h" // Altera的数据类型 |
04 |
#include "sys/alt_alarm.h" // Interval Timer核的驱动头文件 |
05 |
#include "unistd.h" // NULL |
06 |
07 |
// 用户回调函数 |
08 |
alt_u32 timer_CallBackFunc( void * context); |
09 |
10 |
alt_alarm timer_addr; // 指向结构体alt_alarm的指针 |
11 |
alt_u32 ticks_num = 100; // 100 ticks * 1 ms/tick = 100 ms |
12 |
13 |
int main() |
14 |
{ |
15 |
/* |
16 |
* 函数功能:启动sys_clk_timer服务 |
17 |
* 函数备注:#include "sys/alt_alarm.h" |
18 |
* 入口参数:timer_addr,指向结构体alt_alarm的指针 |
19 |
* ticks_num,每隔ticks_num执行一次回调函数 |
20 |
* timer_CallBackFunc,用户回调函数 |
21 |
* context,传给用户回调函数的参数,此处为NULL |
22 |
*/ |
23 |
alt_alarm_start(&timer_addr, ticks_num, timer_CallBackFunc, NULL); |
24 |
while (1); |
25 |
return 0; |
26 |
} |
27 |
28 |
/* |
29 |
* 函数功能:用户回调函数 |
30 |
* 函数备注:用户自己编写 |
31 |
* 入口参数:*context, 从alt_alarm_start()传来的参数 |
32 |
* 出口参数:ticks_num, sys_clk_timer服务的周期数 |
33 |
*/ |
34 |
alt_u32 timer_CallBackFunc( void * context) |
35 |
{ |
36 |
static alt_u8 temp = 0; // static 定义时,只可赋值一次 |
37 |
IOWR_ALTERA_AVALON_PIO_DATA(Q_LED_BASE, temp); |
38 |
temp = ~temp; // 翻转temp |
39 |
return ticks_num; // 返回下一次sys_clk_timer服务的ticks_num |
40 |
} |
对应前面提到的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,查看相关函数的原型。此处由于水平有限,不做相关解析。请读者自行分析。
01 |
/* |
02 |
* "alt_alarm" is a structure type used by applications to register an alarm |
03 |
* callback function. An instance of this type must be passed as an input |
04 |
* argument to alt_alarm_start(). The user is not responsible for initialising |
05 |
* the contents of the instance. This is done by alt_alarm_start(). |
06 |
*/ |
07 |
08 |
typedef struct alt_alarm_s alt_alarm; |
09 |
10 |
/* |
11 |
* alt_alarm_start() can be called by an application/driver in order to register |
12 |
* a function for periodic callback at the system clock frequency. Be aware that |
13 |
* this callback is likely to occur in interrupt context. |
14 |
*/ |
15 |
16 |
extern int alt_alarm_start (alt_alarm* the_alarm, |
17 |
alt_u32 nticks, |
18 |
alt_u32 (*callback) ( void * context), |
19 |
void * context); |
20 |
21 |
/* |
22 |
* alt_alarm_stop() is used to unregister a callback. Alternatively the callback |
23 |
* can return zero to unregister. |
24 |
*/ |
25 |
26 |
extern void alt_alarm_stop (alt_alarm* the_alarm); |
27 |
28 |
/* |
29 |
* Obtain the system clock rate in ticks/s. |
30 |
*/ |
31 |
32 |
static ALT_INLINE alt_u32 ALT_ALWAYS_INLINE alt_ticks_per_second ( void ) |
33 |
{ |
34 |
return _alt_tick_rate; |
35 |
} |
36 |
37 |
/* |
38 |
* alt_sysclk_init() is intended to be only used by the system clock driver |
39 |
* in order to initialise the value of the clock frequency. |
40 |
*/ |
41 |
42 |
static ALT_INLINE int ALT_ALWAYS_INLINE alt_sysclk_init (alt_u32 nticks) |
43 |
{ |
44 |
if (! _alt_tick_rate) |
45 |
{ |
46 |
_alt_tick_rate = nticks; |
47 |
return 0; |
48 |
} |
49 |
else |
50 |
{ |
51 |
return -1; |
52 |
} |
53 |
} |
54 |
55 |
/* |
56 |
* alt_nticks() returns the elapsed number of system clock ticks since reset. |
57 |
*/ |
58 |
59 |
static ALT_INLINE alt_u32 ALT_ALWAYS_INLINE alt_nticks ( void ) |
60 |
{ |
61 |
return _alt_nticks; |
62 |
} |
参考资料
1. Volume5: Embedded Peripherals, QuartusII Handbook Version 9.1