来源:朱有鹏老师的嵌入式课程笔记

文章目录

  • 概述
    • 什么是定时器?
    • 定时器、看门狗、RTC、蜂鸣器的关系
  • S5PV210中的定时器
    • PWM定时器
      • PWM定时器的逻辑框图
      • TCNT&TCNTB&TCNTO、TCMP&TCMPB的区别
      • 定时器的工作流程
      • 定时器的自动装载
      • PWM定时器和蜂鸣器实战
    • 系统定时器
    • 看门狗定时器
      • 看门狗的实战
    • 实时时钟RTC
      • 设置rtc时间的代码示例
      • 获取rtc时间的代码示例
      • 设置rtc闹钟的代码示例

概述

什么是定时器?

定时器是SoC的一个外设,通过内部计数器的数值×时钟周期,实现定时,一旦到达指定时间,就会触发定时器中断,CPU将执行定时器的中断ISR。计数器的工作依赖时钟系统,时钟源为PSYS域的低频时钟分支APB(65M)分频之后的结果。

定时器对于CPU的意义在于,单核CPU在程序间切换依赖定时器的触发。

定时器有一个寄存器TCNT,计时开始时,将计数值写入TCNT,每隔时钟周期,由硬件自动完成计数值-1,直到计数值为0,触发定时器中断。

定时器、看门狗、RTC、蜂鸣器的关系

  • 看门狗:一种定时器,到时间后不止发生定时器中断,还会复位CPU
  • RTC(real time clock):实现实时的时钟系统,CPU初始化的时候为0,每一个CPU时钟周期加1,通过读取它的数值可以获取到CPU运行了多少个时钟周期。
  • 蜂鸣器:蜂鸣器由定时器来驱动

S5PV210中的定时器

S5PV210中有4类定时器。

PWM定时器

最常用的定时器,我们常说的定时器通常指的就是它,它能产生PWM信号。一般用在外设。

S5PV210有5个PWM定时器,其中timer0-3各自对应一个外部GPIO,能够给外部产生PWM信号,timer4没有外部GPIO,用于产生内部中断。5个定时器的时钟源为PCLK_PSYS(外设时钟源的低频分支),timer0-1共用一个预分频器,timer2-4共用一个预分频器,每个timer内部有自己的二级分频器,用以形成每个timer自己的时钟周期。

PWM定时器的逻辑框图

时钟源进来后经过2个8bit的预分频器prescaler(提供1/256 - 1比例的分频功能)分别给timer0-1和timer2-4提供时钟信号,随后经过每一个timer内部的分频器(多选一MUX开关,仅提供图中固定比例的分频系数)之后生成内部时钟信号,然后进入控制逻辑模块,其中TCMPBx用于产生PMW信号,TCNTBx用于计数,随后可以选择直接通过pwm引脚(XpwmTOUTx)输出PWM信号(可以看到timer4没有输出引脚),也可以选择先经过死区生成器后输出,也可以选择先电平翻转后再经过死区生成器后输出。

TCNT&TCNTB&TCNTO、TCMP&TCMPB的区别

TCNT是TCNTB的影子寄存器,它们属于两个寄存器,后者可以被CPU访问支持对计数数值的修改,前者不支持访问开始工作的时候会先从TCNTB读取数值的初始值,然后没过一个时钟周期减一,减一的时候,TCNTB上的数值不会变化,这样设计方便了计数器的重载。TCNTO用于读取TCNT的当前值。总结起来就是TCNT不支持直接读写,通过TCNTB来写,通过TCNTO来读。

TCMP&TCMPPB和定时器本身无关,决定生成PWM波形的占空比,即:

  • PWM波形的周期 = 时钟周期 × TCNTB
  • PWM波形的占空比 = TCMPB ÷ TCNTB

定时器的工作流程

  1. 根据计数周期和所需定时时间,计算TCNT值
  2. 将计算结果写入TCNTB
  3. 将TCNTB刷到TCNT中
  4. 启动定时器开始计时
  5. 计时过程中可以通过TCNTO来读取当前计数值,一般没这个需求。
  6. 计数结束后触发中断,CPU执行相应中断处理函数

定时器的自动装载

重新装载指当定时器结束一轮工作后,如果需要再次执行一轮,需要将TCNTB中的数值重新刷到TCNT中,并重新开启定时器。老的SoC需要手动执行上述步骤,新的SoC会自动执行,也被称为自动装载。

PWM定时器和蜂鸣器实战

蜂鸣器的基本工作原理为,PWM信号接到三极管的基极,当基极为正电压的时候,三极管电路导通,负电压的时候,三极管电路关闭,给多少频率的PWM信号,蜂鸣器就会以多少频率发出声音。

从蜂鸣器的原理图可以看出,PWM信号是从GPD0_2引脚引出,所以首先要通过GPD0CON寄存器设置该引脚的工作模式,TOUT_2表示从PWM输出,因此要将GPD0CON寄存器的[8:11]位设置成0010

让蜂鸣器响的代码如下:

#define  GPD0CON     (0xE02000A0)
#define     TCFG0       (0xE2500000)
#define     TCFG1       (0xE2500004)
#define     CON         (0xE2500008)
#define     TCNTB2      (0xE2500024)
#define     TCMPB2      (0xE2500028)#define     rGPD0CON    (*(volatile unsigned int *)GPD0CON)
#define     rTCFG0      (*(volatile unsigned int *)TCFG0)
#define     rTCFG1      (*(volatile unsigned int *)TCFG1)
#define     rCON        (*(volatile unsigned int *)CON)
#define     rTCNTB2     (*(volatile unsigned int *)TCNTB2)
#define     rTCMPB2     (*(volatile unsigned int *)TCMPB2)// 初始化PWM timer2,使其输出PWM波形:频率是2KHz、duty为50%
void timer2_pwm_init(void)
{// 设置GPD0_2引脚,将其配置为XpwmTOUT_2rGPD0CON &= ~(0xf<<8);rGPD0CON |= (2<<8);// 设置PWM定时器的一干寄存器,使其工作// 先设置预分频器,rTCFG0 &= ~(0xff<<8);rTCFG0 |= (65<<8);            // prescaler1 = 65, 预分频后频率为1MHz//然后设置子分频器rTCFG1 &= ~(0x0f<<8);rTCFG1 |= (1<<8);          // MUX2设置为1/2,分频后时钟周期为500KHz// 时钟设置好,我们的时钟频率是500KHz,对应的时钟周期是2us。也就是说每隔2us// 计一次数。如果要定的时间是x,则TCNTB中应该写入x/2usrCON |= (1<<15);     // 使能auto-reload,反复定时才能发出PWM波形rTCNTB2 = 250;            // 0.5ms/2us = 500us/2us = 250rTCMPB2 = 125;         // duty = 50%//rTCNTB2 = 50;          //rTCMPB2 = 25;    // 第一次需要手工将TCNTB中的值刷新到TCNT中去,以后就可以auto-reload了rCON |= (1<<13);        // 先打开自动刷新功能,然后关闭自动刷新,这样TCNTB中的数据就刷到TCNT中去了rCON &= ~(1<<13);       rCON |= (1<<12);     // 开timer2定时器。要先把其他都设置好才能开定时器
}int main(void)
{timer2_pwm_init();while(1){delay();}return 0;
}

系统定时器

用于给操作系统产生时钟信号(systick),操作系统的时间片调度依赖这个时钟信号。一般用在操作系统。

看门狗定时器

和上面两个定时器原理上没有本质区别,最大区别在于,可以设置时间到了是产生中断还是产生复位信号。看门狗定时器的逻辑框图如下所示:

首先从PCLK获取时钟源信号,经过8-bit的预分频器处理后,输入二级分频器,最后输出作为看门狗定时器的时钟源信号,其中WTCNT类似TCNT,WTDAT类似TCNTB,定时结束后,可以选择触发一个中断,或是输出一个RESET信号。

通过设置WTCON寄存器实现看门狗控制参数的设置,WTCON的参数说明如下所示,常用的参数包括:

  • [15:8]:设置预分频器的分频比

  • [5]:启动看门口

  • [4:3]:设置二级分频器的分频比

  • [2]:是否产生中断

  • [0]:是否产生复位信号

看门狗的实战

#define      WTCON       (0xE2700000)
#define     WTDAT       (0xE2700004)
#define     WTCNT       (0xE2700008)
#define     WTCLRINT    (0xE270000C)#define     rWTCON      (*(volatile unsigned int *)WTCON)
#define     rWTDAT      (*(volatile unsigned int *)WTDAT)
#define     rWTCNT      (*(volatile unsigned int *)WTCNT)
#define     rWTCLRINT   (*(volatile unsigned int *)WTCLRINT)// 初始化WDT使之可以产生中断
void wdt_init_interrupt(void)
{// 第一步,设置好预分频器和分频器,得到时钟周期是128usrWTCON &= ~(0xff<<8);rWTCON |= (65<<8);             // 1MHzrWTCON &= ~(3<<3);rWTCON |= (3<<3);                // 1/128 MHz, T = 128us// 第二步,设置中断和复位信号的使能或禁止rWTCON |= (1<<2);               // enable wdt interruptrWTCON &= ~(1<<0);                // disable wdt reset// 第三步,设置定时时间// WDT定时计数个数,最终定时时间为这里的值×时钟周期rWTDAT = 10000;                    // 定时1.28srWTCNT = 10000;                  // 定时1.28s// 其实WTDAT中的值不会自动刷到WTCNT中去,如果不显式设置WTCON中的值,它的值就是// 默认值,然后以这个默认值开始计数,所以这个时间比较久。如果我们自己显式的// 设置了WTCNT和WTDAT一样的值,则第一次的定时值就和后面的一样了。//rWTDAT = 1000;                    // 定时0.128s//rWTCNT = 1000;                    // 定时0.128s// 第四步,先把所有寄存器都设置好之后,再去开看门狗rWTCON |= (1<<5);                // enable wdt
}

实时时钟RTC

RTC是SoC的内部外设,由独立的晶振提供时钟源,有寄存器专门记录年月日时分秒,并且由独立电池供电以保证开发板断电后RTC依然在工作。RTC和PWM定时器的区别在于,后者关注的是时间段,RTC关注的是时间点。RTC的逻辑框图如下所示:

其中左边的三个黑框是晶振信号,晶振的频率是32.768khz,经过一个2^15的分频器之后,生成1-32K的时钟源信号,经过处理后生成年月日时分秒数据,记录在相应的寄存器中。Alarm Generator是一个基于中断的闹钟发生器,可以设置闹钟时间,到时间后会产生RTC alarm interrupt。

S5PV210涉及到的寄存器包括:

  1. INTP:中断挂起寄存器,RTC中有两个中断,一个是ALARM中断,一个是TIC中断
  2. RTCCON:RTC的控制寄存器
  3. RTCALM:RTC闹钟相关寄存器
  4. BCDxxx:时间寄存器,都以BCD开头,因为时间用的是BCD编码,它是用4bit来表示一个十进制0-9数字的编码方式,比如十进制59,需要转换成0x59,再输入到时间寄存器中。

为了防止时间被乱改,RTC默认是不可写的,如果要写需要通过RTCCON的bit1把读写开关打开。

年月日时分秒的数值分别记录在BCDYEAR、BCDMON、BCDDAY、BCDHOUR、BCDMIN、BCDSEC寄存器中,除了年之外都以2个4bit来记录BCD数值,年比较特殊,用3个4bit来记录数值,表示的是2000往后的年数。

设置rtc时间的代码示例

#define  RTC_BASE     (0xE2800000)
#define     rINTP        (*((volatile unsigned long *)(RTC_BASE + 0x30)))
#define     rRTCCON      (*((volatile unsigned long *)(RTC_BASE + 0x40)))
#define     rTICCNT      (*((volatile unsigned long *)(RTC_BASE + 0x44)))
#define     rRTCALM      (*((volatile unsigned long *)(RTC_BASE + 0x50)))
#define     rALMSEC      (*((volatile unsigned long *)(RTC_BASE + 0x54)))
#define     rALMMIN      (*((volatile unsigned long *)(RTC_BASE + 0x58)))
#define     rALMHOUR     (*((volatile unsigned long *)(RTC_BASE + 0x5c)))
#define     rALMDATE     (*((volatile unsigned long *)(RTC_BASE + 0x60)))
#define     rALMMON      (*((volatile unsigned long *)(RTC_BASE + 0x64)))
#define     rALMYEAR     (*((volatile unsigned long *)(RTC_BASE + 0x68)))
#define     rRTCRST      (*((volatile unsigned long *)(RTC_BASE + 0x6c)))
#define     rBCDSEC      (*((volatile unsigned long *)(RTC_BASE + 0x70)))
#define     rBCDMIN      (*((volatile unsigned long *)(RTC_BASE + 0x74)))
#define     rBCDHOUR     (*((volatile unsigned long *)(RTC_BASE + 0x78)))
#define     rBCDDATE     (*((volatile unsigned long *)(RTC_BASE + 0x7c)))
#define     rBCDDAY      (*((volatile unsigned long *)(RTC_BASE + 0x80)))
#define     rBCDMON      (*((volatile unsigned long *)(RTC_BASE + 0x84)))
#define     rBCDYEAR     (*((volatile unsigned long *)(RTC_BASE + 0x88)))
#define     rCURTICCNT   (*((volatile unsigned long *)(RTC_BASE + 0x90)))
#define     rRTCLVD      (*((volatile unsigned long *)(RTC_BASE + 0x94)))// 函数功能:把十进制num转成bcd码,譬如把56转成0x56
static unsigned int num_2_bcd(unsigned int num)
{// 第一步,把56拆分成5和6 // 第二步,把5和6组合成0x56return (((num / 10)<<4) | (num % 10));
}// 函数功能:把bcd码bcd转成十进制,譬如把0x56转成56
static unsigned int bcd_2_num(unsigned int bcd)
{// 第一步,把0x56拆分成5和6 // 第二步,把5和6组合成56return (((bcd & 0xf0)>>4)*10 + (bcd & (0x0f)));
}struct rtc_time
{unsigned int year;unsigned int month;unsigned int date;        unsigned int hour;          unsigned int minute;unsigned int second;unsigned int day;
};void rtc_set_time(const struct rtc_time *p)
{// 第一步,打开RTC读写开关rRTCCON |= (1<<0);// 第二步,写RTC时间寄存器rBCDYEAR = num_2_bcd(p->year - 2000);rBCDMON = num_2_bcd(p->month);rBCDDATE = num_2_bcd(p->date);rBCDHOUR = num_2_bcd(p->hour);rBCDMIN = num_2_bcd(p->minute);rBCDSEC = num_2_bcd(p->second);rBCDDAY = num_2_bcd(p->day);// 最后一步,关上RTC的读写开关rRTCCON &= ~(1<<0);
}

获取rtc时间的代码示例

void rtc_get_time(struct rtc_time *p)
{// 第一步,打开RTC读写开关rRTCCON |= (1<<0);// 第二步,读RTC时间寄存器p->year = bcd_2_num(rBCDYEAR) + 2000;p->month = bcd_2_num(rBCDMON);p->date = bcd_2_num(rBCDDATE);p->hour = bcd_2_num(rBCDHOUR);p->minute = bcd_2_num(rBCDMIN);p->second = bcd_2_num(rBCDSEC);p->day = bcd_2_num(rBCDDAY);// 最后一步,关上RTC的读写开关rRTCCON &= ~(1<<0);
}

设置rtc闹钟的代码示例

void rtc_set_alarm(void)
{rALMSEC = num_2_bcd(23);//没分钟的23秒触发一次中断rRTCALM |= 1<<0;//开启rtc闹钟总开关rRTCALM |= 1<<6;//开启rtc以秒为单位的闹钟的开关
}

S5PV210中的定时器相关推荐

  1. ARM芯片开发(S5PV210芯片)——定时器、看门狗、RTC

    1.计数器 计数器就是每隔一段固定的时间计数值就加一,于是我们可以根据计数值来计算时间:经过的时间=计数值x计数时间间隔. 2.定时器 2.1.定时器介绍 定时器具有计时的功能,类似于我们手机自带的倒 ...

  2. java怎么加定时器_JAVA WEB程序中添加定时器

    JAVA WEB程序中添加定时器 //这是我的定时器类,用来定时执行某段任务: package com.my.time; import java.text.ParseException; import ...

  3. VC++控制台程序中使用定时器

    我现在项目是一个控制台程序,用到的Win32API都是与界面无关的,今天需要加入定时器刷新的功能,由于没有消息循环,所以WM_TIMER消息应该如何处理呢?综合了下网上找到的资料,写了个简单的demo ...

  4. lwip协议栈中超时定时器实现原理

    lwip协议栈中超时定时器实现原理 1,超时定时器存在的目的: tcpip协议中存在很多需要定时处理的任务,包括一次性超时处理和周期性超时处理. 以tcp传输为例,每条连接总共需要建立七个定时器,依次 ...

  5. 51单片机中的定时器

    文章目录 1 51单片机中的定时器介绍 1.1 定时器的初步认识 1.2 定时器的寄存器 1.3 使用定时器的方法 1.4 定时时间的计算 2 定时器模式1 2.1 定时器模式1原理分析 2.2 使用 ...

  6. linux内核定时器死机,浅析linux内核中timer定时器的生成和sofirq软中断调用流程

    浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...

  7. 嵌入式操作系统内核原理和开发(实时系统中的定时器)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 关于定时器的内容,其实我们之前也讨论过,也书写过相应的代码,但是表达得比较晦涩,效率也比较低. ...

  8. MFC的DLL中实现定时器功能

    方法一:创建一个线程, 反复读系统时间不就可以了? 如果定时要求不严,用Sleep就可以了.DWORD WINAPI TimerThread(LPVOID pamaram) { UINT oldTic ...

  9. swoole 类中使用定时器

    swoole 类中使用定时器  服务端 server <?php class TimerServer {private $serv;public function __construct() { ...

最新文章

  1. 老司机 iOS 周报 #24 | 2018-06-25
  2. C语言 字符串转结构体,字符串指针转化为结构体指针!
  3. 冻存样品对单细胞测序影响大吗?
  4. DreamFactory入门指南 - 第2章安装和配置DreamFactory
  5. python实现第一个web_使用Python的Flask框架来搭建第一个Web应用程序
  6. JS 继承(类式 与 原型式)
  7. [原创]java WEB学习笔记35:java WEB 中关于绝对路径 和相对路径问题
  8. android--04activity的布局管理器
  9. Vue 项目使用 又拍云 云存储服务
  10. mysql有to char函数吗_mysql 类似to_char() to_date()函数
  11. 画太极(echarts)
  12. 你考软考的心路历程是什么?
  13. 【Pytorch深度学习实践】B站up刘二大人之SoftmaxClassifier-代码理解与实现(8/9)
  14. P3951 小凯的疑惑
  15. Ehcache缓存时间设置
  16. 盖茨鲍尔默曾演黑客帝国疯狂嘲讽 Linux
  17. YOLOv4:目标检测的最佳速度和精度
  18. Mirai登陆QQ的登陆验证过程
  19. 数据结构基础知识点,看完保证期末不挂科!
  20. 阿里飞冰使用Link路由跳转报错之“react-router”与“react-router-dom”

热门文章

  1. 几何向量:ScreenToViewportPoint/ScreenToWorldPoint函数解析
  2. 计算机蓝屏代码0x000000ED,电脑蓝屏代码0x000000ed,小编教你解决蓝屏0x000000ed问题...
  3. 软考A计划-电子商务设计师-模拟试题卷一
  4. 数组 选择排序 c语言
  5. mysql聚簇索引存储结构_Mysql聚簇索引和非聚簇索引原理(数据库)
  6. 【压测】基准测试、性能测试、压力测试--Sysbench
  7. 12大理由告诉你选择Java不会后悔!
  8. 制作简单进销存管理系统(C#)
  9. 我的世界服务器网页商城,《我的世界》【PC】版Hypixel服务器商城正式上线啦!...
  10. Java课程设计——房屋出租信息管理系统