S5pv210——timer计时器

1:timer计时器原理

计时器内部实质是一个计数器,计数器工作在一定的时钟频率下,比如200KHz(每秒计数200 000下),计时器内部有一个TCNT寄存器,这是一个down-counter(减计数器)

每个时钟频率减1,我们可以设置TCNT里的数字(比如200 000)当TCNT中的数值减为0时,这段时间正好是1s,此时计时器会产生一个内部中断,我们可以设置中断处理函数

来操作相应的中断。

2:PWM timer原理:

PWM本质上也是一个计时器,只不过他除了TCNT寄存器以外还有一个TCMPBn寄存器,TCMPBn寄存器是一个比较寄存器,当TCNT中的值减到与TCMPBn中的值相等的时候,

PWM控制器会把相应的输出引脚中输出的电平转换(如原来是低电平转换为高电平,实际到底是高电平还是低电平可以通过示波器来查看),从而产生PWM波形。

s5pv210 pwm 框图:下图设置TCNT为159、TCMPBn为109当TCNT中的值大于109时为高电平,小于109是为低电平,一个周期以后,TCNT中的值会自动reload。

死区生成器:

3:看门狗timer的本质也是一个计时器,只不过当时间到了他执行的是reset程序。我们可以通过设置 来让他执行其他程序。

4:RTC:RTC为实时时钟,本质也是一个计时器,只不过在他内部存放了一个时间值作为基准,RTC可以外接纽扣电池,在板子不接电源的情况下一直工作,通过timer中的值和基准时间值,

来计算实时时间。我们可以设置这个基准时间。

5:S5PV210内部的timer介绍 

The S5PV210 has five 32-bit Pulse Width Modulation (PWM) timers. These timers generate internal interrupts for the ARM subsystem. In addition, Timers 0, 1, 2 and 3 include a PWM function, which drives an external I/O signal. The PWM in timer 0 has an optional dead-zone generator capability to support a large current device. Timer 4 is internal timers without output pins.
The Timers use the APB-PCLK as source clock. Timers 0 and 1 share a programmable 8-bit prescaler that provides the first level of division for the PCLK. Timers 2, 3, and 4 share a different 8-bit prescaler. Each timer has its own private clock-divider that provides a second level of clock division (prescaler divided by 2, 4, 8, or 16). Alternatively, the timers can select a clock source from CMU. Timers 0, 1, 2, 3, and 4 select SCLK_PWM.
Each timer has its own 32-bit down-counter which is driven by the timer clock. The down-counter is initially loaded from the Timer Count Buffer register (TCNTBn). If the down-counter reaches zero, the timer interrupt request is generated to inform the CPU that the timer operation is complete. If the timer down-counter reaches zero, the value of corresponding TCNTBn automatically reloads into the down-counter to start a next cycle. However, if the timer stops, for example, by clearing the timer enable bit of TCONn during the timer running mode, the value of TCNTBn is not reloaded into the counter.
The PWM function uses the value of the TCMPBn register. The timer control logic changes the output level if down-counter value matches the value of the compare register in timer control logic. Therefore, the compare register determines the turn-on time (or turn-off time) of a PWM output.
The TCNTBn and TCMPBn registers are double buffered to allow the timer parameters to be updated in the middle of a cycle. The new values do not take effect until the current timer cycle completes.

S5PV210内内部有5个32位脉冲计时器。这些计时器为arm系统产生内部中断。另外,计时器0、1、2、3有pwm功能可以驱动外部的I/O信号。timer0有可选的死区生成器功能支持大电流设备,timer4是内部计时器没有输出引脚。

timers 使用PCLK_PSYS作为时钟源,timer 0、1共用一个可编程的8位的预分频器。timer2、3、4共用另一个8位的预分频器,每个timer都有自己的时钟分频器,来提供二级时钟分频。同时,timer可以选择使用CMU的时钟源,timer1-4可选用SCLK_PWM时钟源

每个timer都有它自己的32位down counter(逐渐计数器),逐渐计数器是由时钟来驱动的。down—counter值最初是由TCNTBn加载的,如果down-counter的值减为0,timer中断请求产生来通知CPU timer操作已经完成。如果

down counter中的值达到0,TCNTBn自动reload到down counter中开始下一个循环。然而,如果timer停止,例如清除TCONn使能,TCNTBn中的值就不会reload如 down counter中。

6:相关寄存器介绍

寄存器TCFG0:预分频器,Prescaler0,设置0、1timer的预分频;Prescaler1,设置2、3、4timer的预分频;PCLC_PSYS时钟为66MHz,一般设置为

寄存器TCFG1:二级分频器可以设置为1/2-1/16

寄存器TCON:设置Timer的autoreload以及开始和结束,和间隔时间

寄存器TCNTB0:TCNTBn设置逐渐计数器的初始值;

寄存器TCMPB0:设置TCMPB的值;

 

寄存器TCNTO0:Timer0 Observation Register

 

 7:timer实战

(1):蜂鸣器

(2):看门狗

(3):RTC

蜂鸣器原理:

I/O接口输出PWM波形,当为高电平时,蜂鸣器导通,低电平是,蜂鸣器截止,以2KHz的频率来驱动蜂鸣器,使他发出声音;

上图为s5pv210的蜂鸣器电路图,接的pwm波形引脚为PWMTOUT2;PWMTOUT2对应GPD0_2这个引脚要设置为TOUT模式;

 蜂鸣器代码实战:

/*
 *        s5pv210 裸机
 *
 *        蜂鸣器
 *
 */
 
#define _REG_GPD0CON        (*(volatile unsigned int *)0xE02000A0)
#define _REG_TCFG0            (*(volatile unsigned int *)0xE2500000) 
#define _REG_TCFG1            (*(volatile unsigned int *)0xE2500004) 
#define _REG_TCNTB2            (*(volatile unsigned int *)0xE2500024) 
#define _REG_TCMPB2            (*(volatile unsigned int *)0xE2500028) 
#define _REG_TCON            (*(volatile unsigned int *)0xE2500008)
 
void buzzer_init(void)
{
    
    //第一步:设置相应的引脚为PWM模式TOUT
    _REG_GPD0CON &= ~(0xF<<8);
    _REG_GPD0CON |= (0x2<<8);
    
    
    
    //第二步:设置时钟
    //设置预分频
    _REG_TCFG0 &= ~(0xFF<<8);
    _REG_TCFG0 |= (65<<8);                    //1级分频以后为66/65+1 = 1MHz
    //设置2级分频
    _REG_TCFG1 &= ~(0xF<<8);
    _REG_TCFG1 |= (0x2<<8);                    //2级分频以后频率为1/4 = 250KHz
    
    //第三部:设置TCNTB寄存器
    //_REG_TCNTB2 = 125;
    _REG_TCNTB2 = 250;
    //第四步:设置TCMPB寄存器
    //_REG_TCMPB2 = 62;
    _REG_TCMPB2 = 125;
    //第一次设置TCNTB2、TCMPB2要手动刷新进去,设置方法把TCON的bit14置1
    _REG_TCON |= (1<<13);
    
    //设置TCON寄存器
    _REG_TCON &= ~(0xF<<12);
    _REG_TCON |= ((0x1<<12) | (0x0<<13) | (0x0<<14) | (0x1<<15));
    
        
    
}

 看门狗:

s5pv210的看门狗计时器框图如下:

时钟PCLK_PSYS经过两级分频得到WDT(watch dog timer)的时钟,在开启看门狗之前必须要先写入WTDAT,设置好中断时间,以后才能使能WDT;

到时产生中断信号;WDT可以中断以后执行中断函数,或者中断以后执行复位信号;

相关寄存器详解:

寄存器WTCON: 设置时钟、使能wdt、中断设置、reset设置

寄存器WTDAT:起始值为0x8000;计数器WTCNT的值在第一次计数的时候为0x8000,WTDAT中的值要在第一次计时结束以后才能reload进入WTCNT中。

 

寄存器:WTCNT 计数器的初始值,因为不能reload所以要在这里设置;

寄存器:WTCLRINT用来清中断,写入任意值可以清中断;

代码实战:这里注意一个问题 

wdt_times 在main函数中初始化为0;由于没有清BSS其值不为0;
/*
 *        s5pv210 裸机
 *
 *        看门狗
 *
 */
 
#include "wdt.h"
#include "interrupt.h"
#include "stdio.h"
extern int wdt_times;

 
void wdt_init(int num)
{
    if (num == WDT_RESET) {
        
        //第一步:设置时钟
        //设置预分频时钟
        _REG_WTCON &= ~(0xFF<<8);    
        _REG_WTCON |= (65<<8);                //设置预分频为66/(65+1) = 1MHz
        
        //设置二级时钟
        _REG_WTCON &= ~(0x3<<3);            //二级分频为16 时钟为1MHz/16 = 62.5KHz 
                                            //每个时钟为0.016ms
        //第二步:
        //使能reset 
        _REG_WTCON |= (0x1<<0);        
        //关闭int 
        _REG_WTCON &= ~(0x1<<2);
        
        //第三步:
        //设置WDT时间                        //设置为500ms的话 值应该为32150
        _REG_WTDAT = 32150;
        //设置WTCNT的值
        _REG_WTCNT = 32150;
        
        //使能看门狗
        _REG_WTCON |= (0x1<<5);
        
    }
    if (num == WDT_INT) {
        
        //第一步:设置时钟
        //设置预分频时钟
        _REG_WTCON &= ~(0xFF<<8);    
        _REG_WTCON |= (65<<8);                //设置预分频为66/(65+1) = 1MHz
        
        //设置二级时钟
        _REG_WTCON &= ~(0x3<<3);            //二级分频为16 时钟为1MHz/16 = 62.5KHz
        
        //第二步:
        //使能int 
        _REG_WTCON |= (0x1<<2);
        //关闭reset
        _REG_WTCON &= ~(0x1<<0);
        
        //第三步:
        //设置WDT时间                        //设置为500ms的话 值应该为32150
        _REG_WTDAT = 32150;
        //设置WTCNT的值
        _REG_WTCNT = 32150;
        
        //使能看门狗
        _REG_WTCON |= (0x1<<5);
    }
    
}



void wdt_isr(void)
{
    
    
    //第一步:真正的中断处理函数
    
    if (wdt_times < 5) {
        printf("wdt_times = %d.
", wdt_times);
        wdt_times++;
    }
    else {
        wdt_init(WDT_RESET);
        printf("---------------end------------
");
        
    }
        
    //第二步:清WDT中断挂起
    _REG_WTCLRINT = 0x1;
    
    //第三步:清4个中断处理函数地址
    clean_vicaddress();
    
    
}

 RTC:

The Real Time Clock (RTC) unit can operate using the backup battery while the system power is off. Although power is off, backup battery can store the time by Second, Minute, Hour, Day of the week, Day, Month, and Year data. The RTC unit works with an external 32.768 kHz crystal and performs the function of alarm.

RTC时钟:32.768 kHz 

下图为RTC结构框图:外部晶振经过clock divider分频(固定分频)得到一个固定时钟,可以改变后边寄存器中的值;

相关寄存器介绍:

 寄存器INTP:清中断

 

寄存器:RTCCON:RTC读写要使能;才可以读写

 

 RTCALM寄存器;中断的使能;

 

ALMSEC、ALMMIN。。。。这个寄存器中写入值,到这个值得时候会发生中断,注意这几个寄存器中的值为BCD码;

 BCDSEC、BCDMIN。。。这几个寄存器是RTC中时分秒年月日中的值

 

代码实战:

  rtc.h

#ifndef _RTC_H
#define _RTC_H

typedef struct rtc {
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int date;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
} rtc_t;

#define _REG_BCDSEC            (*(volatile unsigned int *)0xE2800070)
#define _REG_BCDMIN            (*(volatile unsigned int *)0xE2800074)
#define _REG_BCDHOUR        (*(volatile unsigned int *)0xE2800078)
#define _REG_BCDDAY            (*(volatile unsigned int *)0xE280007C)
#define _REG_BCDDAYWEEK        (*(volatile unsigned int *)0xE2800080)
#define _REG_BCDMON            (*(volatile unsigned int *)0xE2800084)
#define _REG_BCDYEAR        (*(volatile unsigned int *)0xE2800088)
#define _REG_RTCCON            (*(volatile unsigned int *)0xE2800040)


#define _REG_ALMSEC            (*(volatile unsigned int *)0xE2800054)
#define _REG_RTCALM            (*(volatile unsigned int *)0xE2800050)
#define _REG_INTP            (*(volatile unsigned int *)0xE2800030)

extern void set_rtc(rtc_t *p);
extern void read_rtc(rtc_t *p);
extern int bcd_2_num(int num);
extern int num_2_bcd(int num);
extern void rtc_sec_int_init(void);
extern void rtc_isr(void);

#endif

rtc.c

/*
 *        s5pv210 裸机
 *
 *        RTC
 *
 */

#include "rtc.h"
#include "stdio.h"
#include "interrupt.h"
 
 //设置rtc的值
void set_rtc(rtc_t *p)
{
    //使能RTC
    _REG_RTCCON |= (1<<0);
    
    //写rtc
    _REG_BCDYEAR         = num_2_bcd((p->year - 2000));
    _REG_BCDMON          = num_2_bcd(p->mon);
    _REG_BCDDAYWEEK     = num_2_bcd(p->date);
    _REG_BCDDAY          = num_2_bcd(p->day);
    _REG_BCDHOUR          = num_2_bcd(p->hour);
    _REG_BCDMIN          = num_2_bcd(p->min);
    _REG_BCDSEC          = num_2_bcd(p->sec);
    
    //关闭RTC
    _REG_RTCCON &= ~(1<<0);    
    
    
}

//读取rtc的值;
void read_rtc(rtc_t *p)
{
    //使能RTC
    _REG_RTCCON |= (1<<0);
    
    //读RTC
    p->year = bcd_2_num(_REG_BCDYEAR) + 2000;
    p->mon  = bcd_2_num(_REG_BCDMON);
    p->date = bcd_2_num(_REG_BCDDAYWEEK);
    p->day  = bcd_2_num(_REG_BCDDAY);
    p->hour = bcd_2_num(_REG_BCDHOUR);
    p->min  = bcd_2_num(_REG_BCDMIN);
    p->sec  = bcd_2_num(_REG_BCDSEC);
    
    
    //关闭RTC
    _REG_RTCCON &= ~(1<<0);        
}

/*
// 函数功能:把十进制num转成bcd码,譬如把56转成0x56
 int num_2_bcd( int num)
{
    // 第一步,把56拆分成5和6 
    // 第二步,把5和6组合成0x56
    return (((num / 10)<<4) | (num % 10));
}

// 函数功能:把bcd码bcd转成十进制,譬如把0x56转成56
int bcd_2_num(int bcd)
{
    // 第一步,把0x56拆分成5和6 
    // 第二步,把5和6组合成56
    return (((bcd & 0xf0)>>4)*10 + (bcd & (0x0f)));
}

*/

int bcd_2_num(int num)
{
    return ((num & (0xF)) + ((num & 0xf0)>>4) * 10);    
}

int num_2_bcd(int num)
{
    return ((num % 10) + ((num / 10) << 4));    
}


//初始化秒中断
void rtc_sec_int_init(void)
{
    //设置中断发生时候的sec值
    _REG_ALMSEC = num_2_bcd(30);
    //使能中断
    _REG_RTCALM |= (0x1<<0);
    _REG_RTCALM |= (0x1<<6);
}

static i = 0;
void rtc_isr(void)
{
    //中断处理函数
    printf("rtc_int is %d.
", i++);
    
    
    //清中断
    _REG_INTP |= (0x1<<1);
    
    //清函数
    clean_vicaddress();
    
}

main.c

#include "stdio.h"
#include "interrupt.h"
#include "rtc.h"

int wdt_times = 1;

void uart_init(void);

static void delay(void)
{
    volatile unsigned long int i = 900000;
    
    while (i) {
        i--;
    }
    
    
}

int main(void)
{
    rtc_t time = {2017, 2, 1, 5, 7, 46, 20};
    
    //初始化串口
    uart_init();
    
    //初始化异常向量
    vector_table_init();
    
    //初始化中断
    interrupt_init();
    set_rtc(&time);
    
    read_rtc(&time);
    
    printf("__________RTC_INT___________
");
    
    printf("%d-", time.year);
    printf("%d-", time.mon);
    printf("%d-", time.day);
    printf("%d-", time.date);
    printf("%d-", time.hour);
    printf("%d-", time.min);
    printf("%d
", time.sec);
    
    
    
    //初始化rtc中断
    rtc_sec_int_init();    
    
    //设置中断处理函数
    creat_israddr(NUM_RTC_ALARM, rtc_isr);
    
    //使能中断
    int_enable(NUM_RTC_ALARM);
    printf("-----------reset----------
");
    
    while(1)
    {
        printf("%d-", time.year);
        printf("%d-", time.mon);
        printf("%d-", time.day);
        printf("%d-", time.date);
        printf("%d-", time.hour);
        printf("%d-", time.min);
        printf("%d
", time.sec);        
        delay();
        read_rtc(&time);
        
    }
    
    return 0;
}
原文地址:https://www.cnblogs.com/biaohc/p/6358632.html