您的位置:首页 > 其它

STM32移植contiki进阶之二:再叙systick

2013-09-23 13:42 302 查看
在前一篇中,我们提到了contiki需要一个系统嘀嗒,称之为systick,在本篇中,我会结合我的程序,详细的叙述systick。

一、systick初始化

我们在main函数中,有这样一句clock_init();这是对系统时钟的初始化

void clock_init()

{

SysTick_Config(clock_get(CLOCK_SYSTICK)/ CLOCK_SECOND);

}

#define CLOCK_SECOND CLOCK_CONF_SECOND

#define CLOCK_CONF_SECOND 100

static __INLINE uint32_t SysTick_Config(uint32_t ticks)

{

if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);

SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;

NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);

SysTick->VAL = 0;

SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |

SysTick_CTRL_TICKINT_Msk |

SysTick_CTRL_ENABLE_Msk;

return (0);

}

这里涉及到几个寄存器的控制:


SysTick->LOAD 对应硬件寄存器STRELOAD,设置寄存器的重载值。一旦寄存器递减至零,就会从这个寄存器中取出重载值,然后实现下一个systick

SysTick->VAL对应硬件寄存器STCURR,这里当前的值设置为0

SysTick->CTRL对应硬件寄存器STCTRL,这里我们将其设置为7,即:将系统节拍时钟源选择为CPU时钟,系统节拍中断使能,系统节拍计数器使能。

关于重载值的问题,LPC1788官方的文档上面也有详细的阐述:

下列示例说明如何根据不同的系统配置选择系统节拍定时器的值。 在所有示例中,都使用10毫秒的中断间隔进行计算,就像系统节拍定时器的设计用法一样。

示例1)

该示例适用于使用100MHz的CPU时钟(CCLK)运行的系统节拍定时器。

STCTRL=7。当STCTRL为7时会使能定时器及其中断,并选择CCLK作为时钟源。 RELOAD = (cclk / 100) - 1 = 1,000,000 - 1 = 999,999 = 0xF423F

在该例中,不存在舍入误差,所以结果与CCLK一样精确。

同样的,我的CPU时钟频率是120MHz,将此作为systick时钟源,那么

RELOAD=(120,000,000/100) - 1 = 0x124F7F,且不存在误差。

二、systick中断

当systick到来的时候,也就是current value值减为零后,系统会发出一个中断,这个中断就是systick的中断,我们要想通过systick驱动etimer,必须在systick的中断中实现。

下面是systick中断的实现

void SysTick_Handler(void)

{

SCB->ICSR = SCB_ICSR_PENDSTCLR; /*!< Clear pending SysTick bit */

TickCounter++;

if (etimer_pending() && etimer_next_expiration_time() <= TickCounter) //timerlist不为空且还没有etimer到期,则执行etimer_request_poll

{

etimer_request_poll(); //让etimer_process更快地再次获得执行。

}

if (--second_countdown == 0) {

current_seconds++;

second_countdown = CLOCK_SECOND;

}

}

在程序中,我们看到有一个Tick_Counter的变量自加,这个counter就是systick了。

contiki维护着几个counter,一个是Tick_Counter,这个是系统嘀嗒,描述的是系统在一段时间内跑了多少个嘀嗒;一个是current_seconds,这个是系统跑了多少秒。我们知道,一个systick是10ms,那么一个surrent_seconds就是100个系统嘀嗒,这个数字100,正好是我们之前定义的CLOCK_CONF_SECOND 。了解到这一点,我们就明白这段程序后面,second_countdown的作用了。即:先将current_countdown赋值100,每次系统嘀嗒到来的时候,将其值减一,如果减到零了,说明1秒时间到了,那么当前的current_seconds要加一,然后将current_countdown赋值100,重新开始计数。

三、用系统嘀嗒做延迟检测

我们在主函数外,加入一段延迟的代码,用做延时的测试

void clock_delay(unsigned int len)

{

unsigned int i;

for(i = 0; i< len; i++)

{

;

}

}

然后参考前面的contiki的编程模型,写一个延时的进程

PROCESS(hello_world_process, "Hello world");

PROCESS(led_on_process, "led on");

AUTOSTART_PROCESSES(&hello_world_process,NULL);

PROCESS_THREAD(hello_world_process, ev, data)

{

static unsigned short start_time;

static unsigned short end_time;

static unsigned short diff_time;

static unsigned int i ;

PROCESS_BEGIN();

printf("Clock delay test, (1,200,000 x 20) cycles:\r\n");

i = 1;

while(i <= 20) {

start_time = clock_time(); // 记录开始timer

clock_delay(1200000 * i); // 软件延时

end_time = clock_time(); // 记录结束timer

diff_time = end_time - start_time; // 计算差值,单位为tick

printf("Delayed %u CPU tick; %u system ticks =~ %u ms\r\n", 1200000 * i, diff_time, diff_time * 10);

i++;

}

PROCESS_END();

}

测试的结果如下



测试是符合要求的。说明我们的systick已经OK了,进程也没有问题。

细心的你可能会发现:如果系统时钟设置为120MHz,那么我延时1200000次,1200000 / 120M = 10ms,那应该正好是1个systick才对,怎么会是4个systick呢?

问这个问题人都是很仔细的人,说明真正用心去思欧考了。这里我详细说明一下:

操作系统中,有机器周期,时钟周期和指令周期这一说法,一般的,1个指令周期=4个时钟周期=4个机器周期。在本例中,我们延时的时候,用的指令是“;”,其实不管用什么,都指的是指令周期,而机器在响应systick的时候,寄存器的值减一,却是时钟周期。

也就是说:延时一个指令周期,时钟周期要跑4次。所以计算出来的systick当然应该是4而不是1。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: