s3c2440芯片中一共有5个16位的定时器,其中有4个定时器(定时器0~定时器3)具有脉宽调制功能,因此用s3c2440可以很容易地实现PWM功能。下面就具体介绍如何实现PWM功能。
用到的寄存器:
GPBCON:引脚配置寄存器
GPBUP:端口使能上拉寄存器
GPBDAT:端口数据寄存器
TCFG0:配置两个8位预分频器
TCFG1:5路多路选择器和DMA模式选择寄存器
TCNTB0: 定时器0计数缓冲寄存器
TCMPB0: 定时器0比较缓冲寄存器
TCON: 定时器控制寄存器
1、PWM是通过引脚TOUT0~TOUT3输出的,而这4个引脚是与GPB0~GPB3复用的,因此要实现PWM功能首先要把相应的引脚配置成TOUT输出。
rGPBCON &= ~3; //clear 2bits rGPBCON |= 2;//set GPB0 as tout0, pwm output,10为GPB0第二功能,TOUT0的pwm输出
定时器输出时钟频率=PCLK ÷ (prescaler+1) ÷ divider
其中prescaler值由TCFG0决定,divider值由TCFG1决定,而prescaler只能取0~255之间的整数,divider只能取2、4、8和16。比如已知PCLK为50MHz,而我们想得到某一定时器的输出时钟频率为25kHz,则依据公式可以使prescaler等于249,divider等于8。有了这个输出时钟频率,理论上我们通过设置寄存器TCNTBn就可以得到任意与0.04毫秒(1÷25000×1000)成整数倍关系的时间间隔了。例如我们想要得到1秒钟的延时,则使TCNTBn为25000(1000÷0.04)即可。
rTCFG0 &= ~0xff;//clear all rTCFG0 |= 15; //prescaler = 15,15+1=16预分频值 rTCFG1 &= ~0xf;//clear all rTCFG1 |= 2; //mux = 1/8分频值___Tclk=PCLK/(prescaler+1)*mux
3、然后设置脉冲的具体宽度,它的基本原理是通过寄存器TCNTBn来对寄存器TCNTn(内部寄存器)进行配置计数,TCNTn是递减的,如果减到零,则它又会重新装载TCNTBn里的数,重新开始计数,而寄存器TCMPBn作为比较寄存器与计数值进行比较,当TCNTn等于TCMPBn时,TOUTn输出的电平会翻转,而当TCNTn减为零时,电平会又翻转过来,就这样周而复始。因此这一步的关键是设置寄存器TCNTBn和TCMPBn,前者可以确定一个计数周期的时间长度,而后者可以确定方波的占空比。由于s3c2440的定时器具有双缓存,因此可以在定时器运行的状态下,改变这两个寄存器的值,它会在下个周期开始有效。
rTCNTB0 = (PCLK>>7)/freq;//TCNTB0赋值,TCNTO0减为0时会加载TCNTB0的值 rTCMPB0 = rTCNTB0>>1; // 50%占空比TCNTO0递减过程中会与TCMPB0比较,相等时pwm置位
原理图如下:
4、最后就是对PWM的控制,它是通过寄存器TCON来实现的,一般来说每个定时器主要有4个位要配置(定时器0多一个死区位):启动/终止位,用于启动和终止定时器;手动更新位,用于手动更新TCNTBn和TCMPBn,这里要注意的是在开始定时时,一定要把这位清零,否则是不能开启定时器的;输出反转位,用于改变输出的电平方向,使原先是高电平输出的变为低电平,而低电平的变为高电平;自动重载位,用于TCNTn减为零后重载TCNTBn里的值,当不想计数了,可以使自动重载无效,这样在TCNTn减为零后,不会有新的数加载给它,那么TOUTn输出会始终保持一个电平(输出反转位为0时,是高电平输出;输出反转位为1时,是低电平输出),这样就没有PWM功能了,因此这一位可以用于停止PWM。
rTCON &= ~0x1f; //清0~4位 rTCON |= 0xb; //1011_disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0; rTCON &= ~2; //clear manual update bit必须清除通常情况下,也可以将该IO口设置为非第二功能,即普通IO模式,即可停止PWM:
rGPBCON &= ~3; //set GPB0 as output,then stop pwm output rGPBCON |= 1; rGPBDAT &= ~1;//output 0
实现对蜂鸣器声调的控制,频率加减,以及暂停。蜂鸣器接在GPB0.
void Buzzer_Freq_Set( U32 freq ) { rGPBCON &= ~3; //clear 2bits rGPBCON |= 2;//set GPB0 as tout0, pwm output,10为GPB0第二功能,TOUT0的pwm输出 rTCFG0 &= ~0xff;//clear all rTCFG0 |= 15; //prescaler = 15,15+1=16预分频值 rTCFG1 &= ~0xf;//clear all rTCFG1 |= 2; //mux = 1/8分频值___Tclk=PCLK/(prescaler+1)*mux rTCNTB0 = (PCLK>>7)/freq;//TCNTB0赋值,TCNTO0减为0时会加载TCNTB0的值 rTCMPB0 = rTCNTB0>>1; // 50%占空比TCNTO0递减过程中会与TCMPB0比较,相等时pwm置位 rTCON &= ~0x1f; //清0~4位 rTCON |= 0xb; //1011_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,then stop pwm output rGPBCON |= 1; rGPBDAT &= ~1;//output 0 } //***************************[ BOARD BEEP ]******************************* void Beep(U32 freq, U32 ms) { Buzzer_Freq_Set( freq ) ; Delay( ms ) ; Buzzer_Stop() ; } /**************************************************************************** 【功能说明】蜂鸣器PWM测试 ****************************************************************************/ void BUZZER_PWM_Test( void ) { U16 freq = 1000 ;//频率初值 Uart_Printf( "\nBUZZER TEST ( PWM Control )\n" );//打印信息 Uart_Printf( "Press +/- to increase/reduce the frequency of BUZZER !\n" ) ; Uart_Printf( "Press 'ESC' key to Exit this program !\n\n" ); Buzzer_Freq_Set( freq ) ;//pwm设置 while( 1 ) { U8 key = Uart_Getch(); if( key == '+' ) { if( freq < 20000 )//pwm++ freq += 10 ; Buzzer_Freq_Set( freq ) ; } if( key == '-' ) { if( freq > 11 )//pwm-- freq -= 10 ; Buzzer_Freq_Set( freq ) ; } Uart_Printf( "\tFreq = %d\n", freq ) ; if( key == ESC_KEY ) { Buzzer_Stop() ;//pwm stop return ; } } }
效果图: