以下是关于s5pv210的定时器相关的图示,可以看出定时器的时钟来自PCLK的时钟,有2个8bit的预分频,然后还可以再次分频为1,2,4,8,16或者直接选用SCLK_PWM,后面有一个逻辑控制单元,一个是计数器,一个是控制输出管脚的的高低电平。计数器是TCNTBn是一个向下递减的计数器,减到0时产生中断(如果使能中断的话),TCMPBn是比较寄存器,当TCNTBn 的值倒计时到和TCMPBn相等时会使对应的输出管脚输出对应的电平
如图是整个定时器运行的全部过程。
以下是关于如何设置timer的步骤
以下是webee的裸机代码:
#include "timer.h"
#include "int.h"
#include "stdio.h"
#include "key.h"
unsigned int time_val; // 全局定时计数变量
/* 功能:
* 启动某个定时器,如启动定时器0,则调用start_time(0);
*/
void start_timer(unsigned long num)
{
if(num < 2)
{
TCON |= 1<< (num * 8);
}
else
{
TCON |= 1<< ((num + 1 )* 4);
}
}
/* 功能:
* 关闭某个定时器,如关闭定时器3,则调用start_time(3);
*/
void stop_timer(unsigned long num)
{
if(num < 2)
{
TCON &= ~(1<< (num * 8));
}
else
{
TCON &= ~(1<< ((num + 1 )* 4));
}
}
/* 功能:
* 复位并关闭定时器0
*/
void reset_time_val(void)
{
time_val = 0;
stop_timer(0);
}
/* 功能:
* 关闭所有PWM定时器
*/
void stop_all_timer(void)
{
TCON = 0;
}
/* 功能:
* timer0中断的真正的中断处理函数
*/
void isr_timer()
{
TINT_CSTAT = TINT_CSTAT; //清timer0的中断状态寄存器
VIC0ADDR = 0; //清中断控制向量
printf("\nCurrent count time = %d.%d\r",time_val/10,time_val%10); // 打印中断发生次数
time_val++;
}
/*
* 定时器0初始化调用timer_init(0,65,4,6250,0);
* timer_num = 0,uprescaler = 65, udivider = 4 = 0x0100,utcntb=6250,utcmpb = 0.
* 定时器的输入时钟 = PCLK / ( {prescaler value + 1} ) / {divider value}
* = PCLK/(65+1)/16=62500hz
* 本实验定时器0.1秒
*/
void timer_init(unsigned int timer_num,unsigned long uprescaler,unsigned long udivider,unsigned long utcntb,unsigned long utcmpb)
{
unsigned long temp0;
temp0 = TCFG0; //设置预分频系数为66
time_val = 0;
if((timer_num < 2) && (timer_num >= 0)) // 一级预分频参数的设置(timer0,1)
{
temp0 = (temp0 & (~(0xff))) | ((uprescaler-1)<<0);
TCFG0 = temp0;
}
else if((timer_num < 5) && (timer_num >= 2)) // 一级预分频参数的设置(timer2,3,4)
{
temp0 = (temp0 & (~(0xff << 8))) | ((uprescaler-1)<<8);
TCFG0 = temp0;
}
TCFG1 = (TCFG1 & (~(0xf<<4*timer_num))& (~(1<<20))) |(udivider<<4*timer_num); // 二级预分频,这里是16分频
switch(timer_num) // 1s = 62500hz,即每秒计数器计数62500次
{
case 0:
TCNTB0 = utcntb;
TCMPB0 = utcmpb;
break;
case 1:
TCNTB1 = utcntb;
TCMPB1 = utcmpb;
break;
case 2:
TCNTB2 = utcntb;
TCMPB2 = utcmpb;
break;
case 3:
TCNTB3 = utcntb;
TCMPB3 = utcmpb;
break;
case 4:
TCNTB4 = utcntb;
break;
default:
break;
}
TCON |= 1<<(1+(timer_num*8)); //第一次必须手动更新
TCON &= ~(1<<(1+(timer_num*8))); //紧接着要清除手动更新位
TCON |= (1<<(0+(timer_num*8)))|(1<<(3+(timer_num*8))); // 自动加载和启动timer0
TINT_CSTAT = (TINT_CSTAT & (~(1<<timer_num)))|(1<<(timer_num)); // 使能timer0中断
}
main.c
#include "lib.h"
#include "clock.h"
#include "led.h"
#include "uart.h"
#include "timer.h"
#include "int.h"
#include "key.h"
int main(void)
{
led_init(); //初始化LED
sys_clock_init(); //初始化时钟
uart_init(); //初始化串口
int_init(); //初始化中断
key_init();
printf("\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
printf("\n This is a stopwatch test.\n");
printf("\n Write by WebeeA8 member: %d \n",2013);
printf("\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
stop_all_timer(); // 先关闭所有PWM定时器
timer_init(0,65,4,6250,0); // 设置timer0,在里面会启动定时器
while (1);
return 0;
}
中断初始化
#include "int.h"
#include "timer.h"
#include "key.h"
/* 通用中断处理函数 */
void irq_handler(void)
{
void (*isr_p)(void); //定义一个函数指针,这个指针指向一个返回值类型为void,参数为void的函数
if(VIC0IRQSTATUS) //VICxIRQSTATUS = 1 表示有中断发生
isr_p = (void (*)(void))VIC0ADDR; //取出中断服务程序地址
else if(VIC1IRQSTATUS)
isr_p = (void (*)(void))VIC1ADDR; //取出中断服务程序地址
else if(VIC2IRQSTATUS)
isr_p = (void (*)(void))VIC2ADDR; //取出中断服务程序地址
else if(VIC3IRQSTATUS)
isr_p = (void (*)(void))VIC3ADDR; //取出中断服务程序地址
(*isr_p)(); //跳转到真正的中断处理函数
}
void int_init(void)
{
pExceptionIRQ = (unsigned long)IRQ_handle; //设置中断跳转地址
VIC0VECTADDR21 = (unsigned long)isr_timer; //设置中断服务程序地址
VIC0VECTADDR16 = (unsigned long)isr_key;
VIC0INTSELECT &= (~(1<<21))&(~(1<<16)); //将外部中断21、16设为IRQ模式
VIC0INTENABLE |= (1<<21)|(1<<16); //使能中断控制器
}
具体的代码可以参考以下地址:
https://code.csdn.net/snippets/1545702