版权声明:本文为博主原创文章,允许转载请注明。谢谢! <a class="copy-right-url" href=" https://blog.csdn.net/wangweijundeqq/article/details/78776517"> https://blog.csdn.net/wangweijundeqq/article/details/78776517</a>
</div>
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-3019150162.css">
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-3019150162.css">
<div class="htmledit_views" id="content_views">
一、PWM
定时器
1.
S5PV210内部共有5个32bit的PWM定时器。PWM定时器可以生成内部中断。PWM定时器0、1、2、3具有PWM功能,可以驱动外部I/O信号。PWM定时器4是一个无外部引脚的内部定时器。PWM 定时器使用 PCLK_PSYS 作为时钟源。
2.
每个定时器有一个由定时器时钟驱动的32位递减计数器。递减计数器的初始值是由TCNTBn自动装载而获得的。如果递减计数器减到 0 时,定时器发出中断请求通知CPU定时器操作已经完成,当定时器递减计数器到达 0,相应的 TCNTBn的值也会自动的装载到递减计数器中以继续下一次循环操作。 在定时器正在运行模式下通过对TCON的定时器使能位清零,则TCNTBn的值不会自动装载到计数器中。TCMPBn 寄存器的值用于脉宽调制功能(PWM)。当递减计数器的值和定时器控制逻辑单元中的比较寄存器的值匹配时,定时器控制单元会改变输出电平。因此,比较寄存器的值决定了PWM的占空比。当定时器使能,定时器计数存寄存器(TCNTBn)得到一个被装载到递减计数器中的初始值。定时器比较缓存寄存器(TCMPBn)有一个被装载到比较寄存器中用来和递减计数器的值作比较的初始值。 TCNTBn和TCMPBn双缓存特点使得当频率和负荷发生改变时,定时器生成一个稳定的输出
主要控制寄存器:
TCFG0:
预分频器参数设置,范围为0-255,定时器0和1,用 Prescaler0,定时器2,3,4用Prescaler 1,预分频系数为1-256
TCFG1
:分频器参数设置,MUX开关,1/1,1/2,1/4,1/8,1/16
TCON:
定时器的设置
TCNTBn:
定时计数寄存器
TCMPBn:
占空比计数寄存器
TCNTOn:
观察寄存器
1.1 PWM
之蜂鸣器实验
蜂鸣器通过
GPD0_2
(
XpwmTOUT2
)引脚
人的耳朵能听见的声音频率有限制(
20Hz-2KHz
)
GPD0CON(0xE02000A0)
,要把
bit8
~
bit11
设置为
0b0010
(功能选择为
TOUT_2
,就是把这个引脚设置为
PWM
输出功能)
从
GPD0_2
引脚可以反推出使用的是
timer2
这个
PWM
定时器。
//设置GPD0_2引脚,将其配置为XpwmTOUT_2
rGPD0CON &=~(0xf
<<8);
rGPD0CON |=(0x2<<8);
//设置预分频器参数为65,则预分频系数为66,1MHZ
rTCFG0 &=~(0xff<<8);
rTCFG0 |=(65<<8);
//设置分频器参数为1,分频系数为1/2,500KHZ
rTCFG1 &= ~(0x0f<<8);
rTCFG1 |= (1<<8);
//设置自动重载
// 计一次数。如果要定的时间是x,则TCNTB中应该写入x/2us
rCON |= (1<<15);
rTCNTB2 = 250; // 0.5ms/2us = 500us/2us = 250
rTCMPB2 = 125; // duty = 50%
// 第一次需要手工将TCNTB中的值刷新到TCNT中去,以后就可以auto-reload了
rCON |= (1<<13); // 打开自动刷新功能
rCON &= ~(1<<13); // 关闭自动刷新功能
//打开timer2
rCON |= (1<<12);
二.看门狗定时器
看门狗定时器相当于一个普通的 16bit 的定时器,看门狗定时器可以产生 reset信号
相关寄存器:
WTCON:选择中断、复位模式,时钟,如果使用看门狗提供定时器功能,打开中断,关闭复位
WTDAT:看门狗递减计数器的重载值,初始化时不能自动重载
WTCNT:看门狗递减计数器的初始值,初始化时不能从WTDAT自动加载
WTCLRINT:写任意值,清除中断
(1)PCLK_PSYS
经过两级分频后生成
WDT
(
watchdog timer
)的时钟周期,然后把要定的时间写到
WTDAT
寄存器中,刷到
WTCNT
寄存器中去减
1
,减到
0
时(定时时间到)产生复位信号或中断信号。
(2)
典型应用中是配置为产生复位信号,我们应该在
WTCNT
寄存器减到
0
之前给
WTDAT
寄存器中重新写值以喂狗。
void wdt_interrupt_init(void)
{
WTCON &= ~(
0xFF<<
8);
WTCON |= (
65<<
8);
WTCON &= ~(
3<<
3);
WTCON |= (
3<<
3);
rWTCON |= (
1<<
2);
rWTCON &= ~(
1<<
0);
rWTDAT =
1000;
rWTCNT =
1000;
WTCON |= (
1<<
5);}
void isr_wdt(void)
{
static
int i =
0;
printf(
"wdt interrupt, i = %d...", i++);
intc_clearvectaddr();
rWTCLRINT =
1;
}
三.实时时钟RTC
实时时钟在系统电源关闭后使用备份电源。实时时钟使用32.768 kHz的外部晶体振荡器工作,具备闹钟功能。
1.主要控制寄存器:
(1)INTP
中断挂起寄存器
(2)RTCCON RTC
控制寄存器
(3)RTCALM ALMxxx
闹钟功能有关的寄存器
(4)BCDxxx
时间寄存器
2.实时时钟RTC的闹钟编程说明
(1)RTC
中所有的时间(年月日时分秒星期,包括闹钟)都是用
BCD
码编码的。
(2)BCD
码本质上是对数字的一种编码。用来解决这种问题:由
56
得到
0x56
(或者反过来)。也就是说我们希望十进制的
56
可以被编码成
56
(这里的
56
不是十进制
56
,而是两个数字
5
和
6
)
.
(3)BCD
码的作用在于可以将十进制数拆成组成这个十进制数的各个数字的编码,变成编码后就没有位数的限制了。譬如我有一个很大的数
123456789123456789
,如果这个数纯粹当数字肯定超出了
int
的范围,计算机无法直接处理。要想让计算机处理这个数,计算机首先得能表达这个数,表达的方式就是先把这个数转成对应的
BCD
码(
123456789123456789
)
(4)BCD
码在计算机中可以用十六进制的形式来表示。也就是说十进制的
56
转成
BCD
码后是
56
,在计算机中用
0x56
来表达(暂时存储与运算)。
(5)
需要写
2
个函数,一个是
bcd
转十进制,一个是十进制转
bcd
。当我们要设置时间时(譬如要设置为
23
分),我们需要将这个
23
转成
0x23
然后再赋值给相应的寄存器
BCDMIN
;当我们从寄存器
BCDMIN
中读取一个时间时(譬如读取到的是
0x59
),需要将之当作
BCD
码转成十进制再去显示(
0x59
当作
BCD
码就是
59
,转成十进制就是
59
,所以显示就是
59
分)。
设置时间与读取显示时间
(1)
为了安全,默认情况下
RTC
读写是禁止的,此时读写
RTC
的时间是不允许的;当我们
要更改
RTC
时间时,应该先打开
RTC
的读写开关,然后再进行读写操作,操作完了后立即关闭读写开关。
(2)
读写
RTC
寄存器时,一定要注意
BCD
码和十进制之间的转换。
(3)
年的问题。
S5PV210
中做了个设定,
BCDYEAR
寄存器存的并不是完整的年数(譬如今年
2015
年),而是基于
2000
年的偏移量来存储的,譬如今年
2015
年实际存的就是
15
(
2015-2000
)
.
还有些
RTC
芯片是以
1970
年(貌似)为基点来记录的。
主要函数:RTC主要寄存器的读写操作,即基地址+偏移量地址访问的方式
static unsigned int num_2_bcd(unsigned int num)
{
return (((num /
10)<<
4) | (num %
10));
}
static unsigned int bcd_2_num(unsigned int bcd)
{
return (((bcd &
0xf0)>>
4)*
10 + (bcd & (
0x0f)));
}
void rtc_set_time(
const struct rtc_time *p)
{
rRTCCON |= (
1<<
0);
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);
rRTCCON &= ~(
1<<
0);
}
void rtc_get_time(struct rtc_time *p)
{
rRTCCON |= (
1<<
0);
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);
rRTCCON &= ~(
1<<
0);
}