一、PWM定时器
S5PV210有5个PWM定时器,其中0、1、2、3各自对应一个外部GPIO,可以通过这些对应的GPIO产生PWM波形信号输出。5个PWM定时器的时钟源为PCLK_PSYS,Timer0和Timer1共同使用一个预分频器、Timer2、3、4共同使用一个预分频器;每个timer有一个专用的独立的分频器;预分频器和分频器构成了2级分频系统,将PCLK_PSYS两级分频后生成的时钟供给Timer模块作为时钟周期。
S5PV210提供电平翻转器和死区生成器。电平翻转器在电路上的实质就是一个电平取反的部件,在编程上反映为一个寄存器位。写0就关闭输出电平反转,写1就开启输出电平反转。开启后和开启前输出电平刚好高低反转。
PWM有一个应用就是用在功率电路中用来对交流电压进行整流。整流时2路整流分别在正电平和负电平时导通工作,不能同时导通(同时导通会直接短路,瞬间的同时导通都会导致电路烧毁)。大功率的开关电源、逆变器等设备广泛使用了整流技术。特别是逆变器,用SoC的GPIO输出的PWM波形来分别驱动2路整流的IGBT。PWM波形用来做整理时要求不能同时高或低,因为会短路。但是实际电路是不理想的,不可能同时上升/下降沿,所以比较安全的做法是留死区。
关于PWM输出的几个寄存器:
设置引脚输出
设置预分频
TCFG1寄存器,设置分频
TCON寄存器,设置重装载
TCNTB和TCMPB,设置周期和占空比
#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)
void timer2_pwm_init(void) {
rGPD0CON &= ~(0xf<<8);
rGPD0CON |= (2<<8); //将GPD0_2引脚配置为XpwmTOUT_2
rTCFG0 &= ~(0xff<<8);
rTCFG0 |= (65<<8); // prescaler1 = 65, 预分频后频率为1MHz
rTCFG1 &= ~(0x0f<<8);
/* MUX2设置为1/2,分频后时钟周期为500KHz,对应的时钟周期是2us */
rTCFG1 |= (1<<8);
rCON |= (1<<15); // 使能auto-reload,反复定时才能发出PWM波形
//rTCNTB2 = 250; // 0.5ms/2us = 500us/2us = 250
//rTCMPB2 = 125; // duty = 50%
rTCNTB2 = 100;
rTCMPB2 = 99;
// 第一次需要手工将TCNTB中的值刷新到TCNT中去,以后就可以auto-reload了
rCON |= (1<<13); // 打开自动刷新功能
rCON &= ~(1<<13); // 关闭自动刷新功能
rCON |= (1<<12); } // 开timer2定时器。要先把其他都设置好才能开定时器
二、看门狗定时中断
WTCON寄存器
void wdt_init_interrupt(void)
{
// 第一步,设置好预分频器和分频器,得到时钟周期是128us
rWTCON &= ~(0xff<<8);
rWTCON |= (65<<8); // 1MHz
rWTCON &= ~(3<<3);
rWTCON |= (3<<3); // 1/128 MHz, T = 128u
// 第二步,设置中断和复位信号的使能或禁止
rWTCON |= (1<<2); // enable wdt interrupt
rWTCON &= ~(1<<0); // disable wdt reset
// 第三步,设置定时时间
rWTDAT = 1000; // 定时0.128s
rWTCNT = 1000; // 定时0.128s
/* 其实WTDAT中的值不会自动刷到WTCNT中去,如果不显式设置WTCON中的值,
它的值就是,默认值,然后以这个默认值开始计数,所以这个时间比较久。设置了
WTCNT和WTDAT一样的值,则第一次的定时值就和后面的一样了 */
// 第四步,先把所有寄存器都设置好之后,开看门狗
rWTCON |= (1<<5); // enable wdt
}
三、RTC
设置年份
设置月
设置日期
设置小时
设置分
struct rtc_time {
unsigned int year;
unsigned int month;
unsigned int date; // 几号
unsigned int hour;
unsigned int minute;
unsigned int second;
unsigned int day; // 星期几
};
// 函数功能:把十进制num转成bcd码,
static unsigned int num_2_bcd(unsigned int num)
{
return (((num / 10)<<4) | (num % 10));
}
// 函数功能:把bcd码bcd转成十进制,譬如把0x56转成56
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)
{
// 第一步,打开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);
}
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);
}
void rtc_set_alarm(void)
{
rALMSEC = num_2_bcd(23);
rRTCALM |= 1<<0;
rRTCALM |= 1<<6;
}