免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1039 | 回复: 0
打印 上一主题 下一主题

MICRO2440 PWM学习 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-20 09:44 |只看该作者 |倒序浏览
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. 不再产生中断请求。

具体操作:
1PWM是通过引脚TOUT0~TOUT3输出的,而这4个引脚是与GPB0~GPB3复用的,因此要实现PWM功能首先要把相应的引脚配置成TOUT输出。
2、再设置定时器的输出时钟频率,它是以PCLK为基准,再除以用寄存器TCFG0配置的prescaler参数,和用寄存器TCFG1配置的divider参数。
3、然后设置脉冲的具体宽度,它的基本原理是通过寄存器TCNTBn来对寄存器TCNTn(内部寄存器)进行配置计数,TCNTn是递减的,如果减到零,则它又会重新装载TCNTBn里的数,重新开始计数,决定着一个计数周期的时间长度,而寄存器TCMPBn作为比较寄存器与计数值进行比较,当TCNTn等于TCMPBn时,TOUTn输出的电平会翻转,而当TCNTn减为零时,电平会又翻转过来,就这样周而复始。它决定着方波的占空比。
手动更新位,用于手动更新TCNTBnTCMPBn,这里要注意的是在开始定时时,一定要把这位清零,否则是不能开启定时器的;


关键点:
一个计数脉冲执行时间 = 1/【 PCLK/(预分频+1)/分割值 】
    = 1/【(50MHZ/(3+1)/2)】 = 1/6.25MHZ
//TCNTB=定时初值: 决定一个PWM脉冲的周期|_|`|
//TCMPB:一个PWM脉冲中的占空比
//TCON:设置顺序重要,首先为手动更新初值,
        然后设为自动更新初值,这样才能启动



  1. /***************************************************
  2. 一个计数脉冲执行时间=1/【 PCLK/(预分频+1)/分割值 】
  3.                     =    (50MHZ/(3+1)/2) = 1/6.25MHZ
  4. //TCNTB=定时初值: 决定一个PWM脉冲的周期|_|`|
  5. //TCMPB:一个PWM脉冲中的占空比
  6. //TCON:设置顺序重要,首先为手动更新初值,
  7.         然后设为自动更新初值,这样才能启动
  8. ***************************************************/
  9. //最为精确的1ms延时程序,使用定时器3
  10. /**************************************************/
  11. void Delay(int time)
  12. {                            //PCLK=50MHZ
  13.     U32 val = (PCLK>>3)/1000-1;    //计数初值6250次,减一计数
  14.     
  15.     rTCFG0 &= ~(0xff<<8);    
  16.     rTCFG0 |= 3<<8;            //prescaler = 3+1,预分频值
  17.     rTCFG1 &= ~(0xf<<12);
  18.     rTCFG1 |= 0<<12;        //mux = 1/2,分割值
  19.     
  20.     //计数器的输入时钟(CCLK)频率:PCLK/(预分频+1)/分割值
  21.     //执行一次初值减一的时间(CCLK)= 1/(50MHZ/(3+1)/2) = 1/6.25MHZ
  22.     //那么完成一次定时所需的时间 = 6250次/6.25MHZ = 1ms = 一个PWM脉冲的周期|_|`|
  23.     rTCNTB3 = val;
  24.     rTCMPB3 = val>>1;        // 50% = PWM,即占空比
  25.     rTCON &= ~(0xf<<16);
  26.     rTCON |= 0xb<<16;        //interval, inv-off, update TCNTB3&TCMPB3, start timer 3
  27.     rTCON &= ~(2<<16);        //clear manual update bit
  28.     while(time--) {
  29.         while(rTCNTO3>=val>>1);        //TCNTO减一初值状态查看
  30.         while(rTCNTO3<val>>1);        //好好体会这两个个WHILE,有意思
  31.     };                                //1ms,一毫秒的检测
  32. }




  33. //***************************[ Buzzer_Freq_Set ]*******************************
  34. void Buzzer_Freq_Set( U32 freq )
  35. {
  36.     rGPBCON &= ~3;            //set GPB0 as tout0, pwm output
  37.     rGPBCON |= 2;
  38.         
  39.     rTCFG0 &= ~0xff;
  40.     rTCFG0 |= 15;            //prescaler = 15+1
  41.     rTCFG1 &= ~0xf;
  42.     rTCFG1 |= 2;            //mux = 1/8
  43.     rTCNTB0 = (PCLK>>7)/freq;
  44.     rTCMPB0 = rTCNTB0>>1;    // 50%
  45.     rTCON &= ~0x1f;
  46.     rTCON |= 0xb;        //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
  47.     rTCON &= ~2;            //clear manual update bit
  48. }



  49. void Buzzer_Stop( void )
  50. {
  51.     rGPBCON &= ~3;            //set GPB0 as output
  52.     rGPBCON |= 1;
  53.     rGPBDAT &= ~1;
  54. }

  1. void Main(void)
  2. {    
  3.     int freq = 1000 ;    
  4.     
  5.     pll_();
  6.     
  7.     Uart_Init( 0,115200 );    //我们只使用UART0,无aFIFO
  8.     Delay(1000);
  9.     Uart_Printf("\n");
  10.     Delay(1000);
  11.     Uart_Printf("BEEP测试!\n");
  12.     Uart_Printf(" + 增加亮度!\n");
  13.     Uart_Printf(" - 减小亮度!\n");
  14.     Uart_Printf(" ? 退出\n");
  15.     
  16.     
  17.     while(1)    //查询收发
  18.     {
  19.         Uart_Getch();    //从PC端接收
  20.         Uart_SendByte(data);    //发送给PC
  21.         Uart_Printf("\n");
  22.         Uart_Printf("数据已经接收\n");     
  23.         
  24.         if( data == '+' )
  25.         {
  26.             if( freq < 20000 )
  27.                 freq += 10 ;
  28.                 
  29.             Buzzer_Freq_Set( freq ) ;
  30.         }

  31.         if( data == '-' )
  32.         {
  33.             if( freq > 11 )
  34.                 freq -= 10 ;
  35.                 
  36.             Buzzer_Freq_Set( freq ) ;
  37.         }
  38.                                   
  39.         if(data == '?')
  40.         {    
  41.             Uart_Printf("\n");
  42.             Uart_Printf("你已经退出!\n");
  43.             Buzzer_Stop();    
  44.             break;
  45.         }          
  46.     }    
  47. }


您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP