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。
一、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。
相关文章推荐
- STM32移植contiki进阶之一:从头开始,从main开始
- STM32移植contiki进阶之三(中):timer 中文版
- STM32移植contiki进阶之三(上):timer
- STM32移植contiki入门之二:简单编程点亮LED灯
- [置顶] STM32移植contiki进阶之三(中):timer 中文版
- STM32移植contiki 从入门到进阶学习
- 基于keil的contiki系统的stm32移植
- STM32移植systick定时器时出现错物error:#20:identifier"IRQn_Type"isundefined
- Contiki STM32移植
- ucos-II移植到stm32上关于systick部分的修改
- STM32移植contiki入门之一:系统介绍和开发环境搭建
- STM32移植contiki入门之三:从LED灯程序到contiki编程模型
- Contiki 在STM32 中的移植
- STM32之用SysTick做准确定时
- stm32 移植cJson 注意free释放内存!!
- STM32学习笔记之Systick
- 关于STM32的systick定时器的详细说明
- Hello China操作系统STM32移植指南(二)
- 移植FreeModbus+ModbusMaster(主机)+STM32至RT-Thread(1、2阶段)
- 关于STM32的systick定时器的详细说明