本文首先介绍“龙芯1c库”中封装的systick系统滴答定时器相关的几个函数,然后通过读取tick数来测试相关接口函数是否正常工作,然后是systick滴答定时器的一些介绍知识,最后才讲解如何封装这几个接口函数的。
“龙芯1c库”是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库,完整源码请移步到https://gitee.com/caogos/OpenLoongsonLib1c
接口简介
滴答定时器初始化——sys_tick_init()
函数原型
// 滴答定时器初始化 void sys_tick_init(unsigned int tick)
入参为每秒钟的中断次数
使用示例
// 每秒的产生的tick数 #define TICK_PER_SECOND (1000) unsigned int tick_per_second = TICK_PER_SECOND; sys_tick_init(tick_per_second); // 每秒产生1000个tick
获取tick值——sys_tick_get()
函数原型
// 获取tick值 unsigned long sys_tick_get(void)
使用示例
unsigned long tick = 0; tick = sys_tick_get();
禁止滴答定时器——sys_tick_disable()
函数原型
// 禁止滴答定时器 void sys_tick_disable(void)
使用示例
直接调用即可,如下
sys_tick_disable();
使能滴答定时器——sys_tick_enable()
函数原型
// 使能滴答定时器 void sys_tick_enable(void)
使用示例
直接调用即可,如下
sys_tick_enable();
在函数sys_tick_init()中,已经调用了sys_tick_enable(),这里再单独提出来的目的是为了和sys_tick_disable()配套。
滴答定时器中断处理函数——sys_tick_handler()
因为滴答定时器比较特殊,默认已经把该中断处理函数注册了,中断发生后会自动调用该函数进行处理。这里提一下的目的是为了一些高级用户,他们可能需要修改中断处理函数中的内容。
默认的中断程序中,会维护一个计数器,每次中断就加一。
测试示例
一般情况下,滴答定时器被用于产生一个固定的时钟节拍。比如每1ms触发一次中断,产生一个节拍。对应一些要求不太高的延时,可以读取系统的当前tick值来实现延时,实际上操作系统就是这样做的。
这里的测试思路是,在初始化后,观察禁止和使能后,当前tick值是否发生变化;观察当改变定时时间后,当前tick值变化的快慢。测试源码如下
// 测试滴答定时器的源文件 #include "../lib/ls1c_public.h" #include "../lib/ls1c_sys_tick.h" #include "../lib/ls1c_delay.h" // 每秒的产生的tick数 #define TICK_PER_SECOND (1000) /* * 测试滴答定时器 */ void test_sys_tick(void) { int i; unsigned int tick_per_second = TICK_PER_SECOND; // 滴答定时器初始化 // 每次滴答定时器中断就会将tick加一,这里只需要读取tick值,并打印出来即可 sys_tick_init(tick_per_second); // 每秒产生1000个tick printf("sys tick init ok! tick = %u\r\n", tick_per_second); for (i=0; i<5; i++) { printf("[%s] tick=%d\r\n", __FUNCTION__, sys_tick_get()); delay_s(1); } // 禁止滴答定时器后,看看tick值是否变化 sys_tick_disable(); printf("sys tick disable\r\n"); for (i=0; i<5; i++) { printf("[%s] tick=%d\r\n", __FUNCTION__, sys_tick_get()); delay_s(1); } // 重新使能滴答定时器后,再看看tick是否变化 sys_tick_enable(); printf("sys tick enable\r\n"); for (i=0; i<5; i++) { printf("[%s] tick=%d\r\n", __FUNCTION__, sys_tick_get()); delay_s(1); } // 修改tick tick_per_second = TICK_PER_SECOND/2; sys_tick_disable(); sys_tick_init(tick_per_second); printf("modify tick = %u\r\n", tick_per_second); for (i=0; i<5; i++) { printf("[%s] tick=%d\r\n", __FUNCTION__, sys_tick_get()); delay_s(1); } while (1) ; }
龙芯1c的systick系统滴答定时器简介
滴答定时器是通用的,每个操作系统都需要的功能。操作系统中的任务切换就是依靠滴答定时器来实现的。简单来说就是滴答定时器定时(比如1ms)产生中断,操作系统在滴答定时器的中断处理函数中判断是否需要切换任务,切换到那个任务,如果需要切换则执行切换。
正因为这个功能这么通用,不是龙芯CPU特有的,所以在目前的龙芯1c芯片手册中也没有相关介绍。那我是怎么知道这些细节的呢?我是在移植中断那部分代码时发现的,后面又在《see mips run》中发现了相关介绍,如下
滴答定时器是由寄存器Count和寄存器Compare组成的。并且位于协处理器0上。
正因为滴答定时器是位于协处理器0上,并且所有MIPS CPU通用的,所以滴答定时器的中断也是MIPS CPU通用的,并且定好了——协处理器0的状态寄存器(SR)中的IM7用于屏蔽或使能滴答定时器,协处理器0的原因寄存器(CAUSE)中的IP7用于判断是否发生了滴答定时器中断。也就是说,滴答定时器中断的使能又协处理器0的状态寄存器(SR)中的IM7和IE两个位来控制。其中IE为全局使能位,正常启动后,IE会被使能,所以通常使用IM7来使能或禁止滴答定时器中断。
封装systick系统滴答定时器接口
滴答定时器的原理和源码都很简单,这里直接把源码贴出来
ls1c_sys_tick.h
// 系统滴答定时器相关接口 #ifndef __OPENLOONGSON_SYS_TICK_H #define __OPENLOONGSON_SYS_TICK_H // 滴答定时器初始化 void sys_tick_init(unsigned int tick); // 禁止滴答定时器 void sys_tick_disable(void); // 使能滴答定时器 void sys_tick_enable(void); // 时钟中断处理函数 void sys_tick_handler(void); // 获取tick值 unsigned long sys_tick_get(void); #endif
ls1c_sys_tick.c
// 系统滴答定时器相关接口 #include "ls1c_mipsregs.h" #define CPU_HZ (252 * 1000000) static volatile unsigned long system_tick; // 禁止滴答定时器 void sys_tick_disable(void) { unsigned int status = 0; status = read_c0_status(); status &= (~STATUSF_IP7); write_c0_status(status); return ; } // 使能滴答定时器 void sys_tick_enable(void) { unsigned int status = 0; status = read_c0_status(); status |= STATUSF_IP7; write_c0_status(status); return ; } // 滴答定时器初始化 void sys_tick_init(unsigned int tick) { // 设置定时时间 write_c0_compare(CPU_HZ/2/tick); write_c0_count(0); // 使能 sys_tick_enable(); return ; } void sys_tick_increase(void) { ++system_tick; } // 滴答定时器中断处理函数 void sys_tick_handler(void) { unsigned int count; count = read_c0_compare(); write_c0_compare(count); write_c0_count(0); sys_tick_increase(); return ; } // 获取tick值 unsigned long sys_tick_get(void) { return system_tick; }
感谢阅读!