S5PV210中的定时器
来源:朱有鹏老师的嵌入式课程笔记
文章目录
- 概述
- 什么是定时器?
- 定时器、看门狗、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
定时器的工作流程
- 根据计数周期和所需定时时间,计算TCNT值
- 将计算结果写入TCNTB
- 将TCNTB刷到TCNT中
- 启动定时器开始计时
- 计时过程中可以通过TCNTO来读取当前计数值,一般没这个需求。
- 计数结束后触发中断,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涉及到的寄存器包括:
- INTP:中断挂起寄存器,RTC中有两个中断,一个是ALARM中断,一个是TIC中断
- RTCCON:RTC的控制寄存器
- RTCALM:RTC闹钟相关寄存器
- 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中的定时器相关推荐
- ARM芯片开发(S5PV210芯片)——定时器、看门狗、RTC
1.计数器 计数器就是每隔一段固定的时间计数值就加一,于是我们可以根据计数值来计算时间:经过的时间=计数值x计数时间间隔. 2.定时器 2.1.定时器介绍 定时器具有计时的功能,类似于我们手机自带的倒 ...
- java怎么加定时器_JAVA WEB程序中添加定时器
JAVA WEB程序中添加定时器 //这是我的定时器类,用来定时执行某段任务: package com.my.time; import java.text.ParseException; import ...
- VC++控制台程序中使用定时器
我现在项目是一个控制台程序,用到的Win32API都是与界面无关的,今天需要加入定时器刷新的功能,由于没有消息循环,所以WM_TIMER消息应该如何处理呢?综合了下网上找到的资料,写了个简单的demo ...
- lwip协议栈中超时定时器实现原理
lwip协议栈中超时定时器实现原理 1,超时定时器存在的目的: tcpip协议中存在很多需要定时处理的任务,包括一次性超时处理和周期性超时处理. 以tcp传输为例,每条连接总共需要建立七个定时器,依次 ...
- 51单片机中的定时器
文章目录 1 51单片机中的定时器介绍 1.1 定时器的初步认识 1.2 定时器的寄存器 1.3 使用定时器的方法 1.4 定时时间的计算 2 定时器模式1 2.1 定时器模式1原理分析 2.2 使用 ...
- linux内核定时器死机,浅析linux内核中timer定时器的生成和sofirq软中断调用流程
浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...
- 嵌入式操作系统内核原理和开发(实时系统中的定时器)
[ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 关于定时器的内容,其实我们之前也讨论过,也书写过相应的代码,但是表达得比较晦涩,效率也比较低. ...
- MFC的DLL中实现定时器功能
方法一:创建一个线程, 反复读系统时间不就可以了? 如果定时要求不严,用Sleep就可以了.DWORD WINAPI TimerThread(LPVOID pamaram) { UINT oldTic ...
- swoole 类中使用定时器
swoole 类中使用定时器 服务端 server <?php class TimerServer {private $serv;public function __construct() { ...
最新文章
- 老司机 iOS 周报 #24 | 2018-06-25
- C语言 字符串转结构体,字符串指针转化为结构体指针!
- 冻存样品对单细胞测序影响大吗?
- DreamFactory入门指南 - 第2章安装和配置DreamFactory
- python实现第一个web_使用Python的Flask框架来搭建第一个Web应用程序
- JS 继承(类式 与 原型式)
- [原创]java WEB学习笔记35:java WEB 中关于绝对路径 和相对路径问题
- android--04activity的布局管理器
- Vue 项目使用 又拍云 云存储服务
- mysql有to char函数吗_mysql 类似to_char() to_date()函数
- 画太极(echarts)
- 你考软考的心路历程是什么?
- 【Pytorch深度学习实践】B站up刘二大人之SoftmaxClassifier-代码理解与实现(8/9)
- P3951 小凯的疑惑
- Ehcache缓存时间设置
- 盖茨鲍尔默曾演黑客帝国疯狂嘲讽 Linux
- YOLOv4:目标检测的最佳速度和精度
- Mirai登陆QQ的登陆验证过程
- 数据结构基础知识点,看完保证期末不挂科!
- 阿里飞冰使用Link路由跳转报错之“react-router”与“react-router-dom”
热门文章
- 几何向量:ScreenToViewportPoint/ScreenToWorldPoint函数解析
- 计算机蓝屏代码0x000000ED,电脑蓝屏代码0x000000ed,小编教你解决蓝屏0x000000ed问题...
- 软考A计划-电子商务设计师-模拟试题卷一
- 数组 选择排序 c语言
- mysql聚簇索引存储结构_Mysql聚簇索引和非聚簇索引原理(数据库)
- 【压测】基准测试、性能测试、压力测试--Sysbench
- 12大理由告诉你选择Java不会后悔!
- 制作简单进销存管理系统(C#)
- 我的世界服务器网页商城,《我的世界》【PC】版Hypixel服务器商城正式上线啦!...
- Java课程设计——房屋出租信息管理系统