S3C2440A 有5 个16 位定时器。其中定时器0、1、2 和3 具有脉宽调制(PWM)功能。
定时器4 是一个无输出引脚的内部定时器
定时器0 和1 共用一个8 位预分频器,定时器2、3 和4 共用另外的8 位预分频器。最大为256
每个定时器有它自己的16 位递减计数器。当递减计数器到达零时,产生定时器中断,初值最大65536
TCNTBn 的值将自动被加载到递减计数器,
TCMPBn 的值是用于脉宽调制(PWM)。当递减计数器的值与定时器控制逻辑中的比较寄存器的值相匹配时定
时器控制逻辑改变输出电平。
当TCNTn 到达0 时,如果中断为使能则将发生一个中断请求。
如何启动一个定时器:
1) 初始值写入到TCNTBn 和TCMPBn 中。
2) 设置相应定时器的手动更新位。推荐你配制变相开/关位。(无论是否使用变换极性)
3) 设置相应定时器的开始位来启动定时器(并且清除手动更新位)。
如果定时器被强制停止,TCNTn 保持计数器值并且不会从TCNTBn 重载。如果需要设置一个新值,执行手动
更新。
一个典型流程:
1. 使能自动重载功能。设置TCNTBn并且设置TCMPBn。置位手动更新位并且配制变相位(开/关)。手动更新位分别设置TCNTn 和TCMPn 到TCNTBn 和TCMPBn 的值中。然后分别设置TCNTBn和TCMPBn 以决定下次重载值。
2. 设置启动位,预设手动更新位为0,变相位为关,自动重载位为开。定时器在定时器分辨率内的等待时间后启动递减计数。
3. 当TCNTn 与TCMPn 的值相同时,TOUTn 的逻辑电平从低电平变为高电平。
4. 当TCNTn 到达0 时,发出中断请求并且TCNTBn 的值加载到暂存器中。在下一个定时器标记时刻,重载TCNTn为暂存器(TCNTBn)的值。
5. 中断服务程序(ISR)中,为下一个持续时间分别设置TCNTBn 和TCMPBn。
6. 当TCNTn 与TCMPn 的值相同时,TOUTn 的逻辑电平从低电平变为高电平。
7. 当当TCNTn 到达0 时,触发一个中断自动重载TCNTn 为TCNTBn 的值。
8. 中断服务程序(ISR)中,禁止自动重载和中断请求以停止定时器。
9. 当TCNTn 与TCMPn 的值相同时,TOUTn 的逻辑电平从低电平变为高电平。
10. 尽管TCNTn 到达0,但因为禁止了自动重载,所以TCNTn 并不会再次重载并且定时器已经停止了。
11. 不再产生中断请求。
具体操作:
1、PWM是通过引脚TOUT0~TOUT3输出的,而这4个引脚是与GPB0~GPB3复用的,因此要实现PWM功能首先要把相应的引脚配置成TOUT输出。
2、再设置定时器的输出时钟频率,它是以PCLK为基准,再除以用寄存器TCFG0配置的prescaler参数,和用寄存器TCFG1配置的divider参数。
3、然后设置脉冲的具体宽度,它的基本原理是通过寄存器TCNTBn来对寄存器TCNTn(内部寄存器)进行配置计数,TCNTn是递减的,如果减到零,则它又会重新装载TCNTBn里的数,重新开始计数,决定着一个计数周期的时间长度,而寄存器TCMPBn作为比较寄存器与计数值进行比较,当TCNTn等于TCMPBn时,TOUTn输出的电平会翻转,而当TCNTn减为零时,电平会又翻转过来,就这样周而复始。它决定着方波的占空比。
手动更新位,用于手动更新TCNTBn和TCMPBn,这里要注意的是在开始定时时,一定要把这位清零,否则是不能开启定时器的;
关键点:
一个计数脉冲执行时间 = 1/【 PCLK/(预分频+1)/分割值 】
= 1/【(50MHZ/(3+1)/2)】 = 1/6.25MHZ
//TCNTB=定时初值: 决定一个PWM脉冲的周期|_|`|
//TCMPB:一个PWM脉冲中的占空比
//TCON:设置顺序重要,首先为手动更新初值,
然后设为自动更新初值,这样才能启动
- /***************************************************
-
一个计数脉冲执行时间=1/【 PCLK/(预分频+1)/分割值 】
-
= (50MHZ/(3+1)/2) = 1/6.25MHZ
-
//TCNTB=定时初值: 决定一个PWM脉冲的周期|_|`|
-
//TCMPB:一个PWM脉冲中的占空比
-
//TCON:设置顺序重要,首先为手动更新初值,
- 然后设为自动更新初值,这样才能启动
-
***************************************************/
- //最为精确的1ms延时程序,使用定时器3
- /**************************************************/
-
void Delay(int time)
-
{ //PCLK=50MHZ
-
U32 val = (PCLK>>3)/1000-1; //计数初值6250次,减一计数
-
-
rTCFG0 &= ~(0xff<<8);
-
rTCFG0 |= 3<<8; //prescaler = 3+1,预分频值
-
rTCFG1 &= ~(0xf<<12);
-
rTCFG1 |= 0<<12; //mux = 1/2,分割值
-
-
//计数器的输入时钟(CCLK)频率:PCLK/(预分频+1)/分割值
-
//执行一次初值减一的时间(CCLK)= 1/(50MHZ/(3+1)/2) = 1/6.25MHZ
-
//那么完成一次定时所需的时间 = 6250次/6.25MHZ = 1ms = 一个PWM脉冲的周期|_|`|
-
rTCNTB3 = val;
-
rTCMPB3 = val>>1; // 50% = PWM,即占空比
-
rTCON &= ~(0xf<<16);
-
rTCON |= 0xb<<16; //interval, inv-off, update TCNTB3&TCMPB3, start timer 3
-
rTCON &= ~(2<<16); //clear manual update bit
-
while(time--) {
-
while(rTCNTO3>=val>>1); //TCNTO减一初值状态查看
-
while(rTCNTO3<val>>1); //好好体会这两个个WHILE,有意思
-
}; //1ms,一毫秒的检测
-
}
-
-
-
-
-
//***************************[ Buzzer_Freq_Set ]*******************************
-
void Buzzer_Freq_Set( U32 freq )
-
{
-
rGPBCON &= ~3; //set GPB0 as tout0, pwm output
-
rGPBCON |= 2;
-
-
rTCFG0 &= ~0xff;
-
rTCFG0 |= 15; //prescaler = 15+1
-
rTCFG1 &= ~0xf;
-
rTCFG1 |= 2; //mux = 1/8
-
rTCNTB0 = (PCLK>>7)/freq;
-
rTCMPB0 = rTCNTB0>>1; // 50%
-
rTCON &= ~0x1f;
-
rTCON |= 0xb; //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
-
rTCON &= ~2; //clear manual update bit
-
}
-
-
-
-
void Buzzer_Stop( void )
-
{
-
rGPBCON &= ~3; //set GPB0 as output
-
rGPBCON |= 1;
-
rGPBDAT &= ~1;
-
}
- void Main(void)
-
{
-
int freq = 1000 ;
-
-
pll_();
-
-
Uart_Init( 0,115200 ); //我们只使用UART0,无aFIFO
-
Delay(1000);
-
Uart_Printf("\n");
-
Delay(1000);
-
Uart_Printf("BEEP测试!\n");
-
Uart_Printf(" + 增加亮度!\n");
-
Uart_Printf(" - 减小亮度!\n");
-
Uart_Printf(" ? 退出\n");
-
-
-
while(1) //查询收发
-
{
-
Uart_Getch(); //从PC端接收
-
Uart_SendByte(data); //发送给PC
-
Uart_Printf("\n");
-
Uart_Printf("数据已经接收\n");
-
-
if( data == '+' )
-
{
-
if( freq < 20000 )
-
freq += 10 ;
-
-
Buzzer_Freq_Set( freq ) ;
-
}
-
-
if( data == '-' )
-
{
-
if( freq > 11 )
-
freq -= 10 ;
-
-
Buzzer_Freq_Set( freq ) ;
-
}
-
-
if(data == '?')
-
{
-
Uart_Printf("\n");
-
Uart_Printf("你已经退出!\n");
-
Buzzer_Stop();
-
break;
-
}
-
}
-
}
阅读(1944) | 评论(0) | 转发(0) |