TQ2440上的PWM实验(裸机)

来源:互联网 发布:android直播软件 编辑:程序博客网 时间:2024/05/08 19:52

原文:http://m.blog.csdn.net/blog/huangzhirong19893311/6867485

PWM(Pulse Width Modulation)——脉宽调制,它是利用微控制器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用于测量、通信、功率控制与变换等许多领域。

s3c2440芯片中一共有5个16位的定时器,其中有4个定时器(定时器0~定时器3)具有脉宽调制功能,因此用s3c2440可以很容易地实现PWM功能。下面就具体介绍如何实现PWM功能。

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,前者可以确定一个计数周期的时间长度,而后者可以确定方波的占空比。由于s3c2440的定时器具有双缓存,因此可以在定时器运行的状态下,改变这两个寄存器的值,它会在下个周期开始有效。

4、最后就是对PWM的控制,它是通过寄存器TCON来实现的,一般来说每个定时器主要有4个位要配置(定时器0多一个死区位):启动/终止位,用于启动和终止定时器;手动更新位,用于手动更新TCNTBn和TCMPBn,这里要注意的是在开始定时时,一定要把这位清零,否则是不能开启定时器的;输出反转位,用于改变输出的电平方向,使原先是高电平输出的变为低电平,而低电平的变为高电平;自动重载位,用于TCNTn减为零后重载TCNTBn里的值,当不想计数了,可以使自动重载无效,这样在TCNTn减为零后,不会有新的数加载给它,那么TOUTn输出会始终保持一个电平(输出反转位为0时,是高电平输出;输出反转位为1时,是低电平输出),这样就没有PWM功能了,因此这一位可以用于停止PWM。

PWM有很多用途,在这里我利用开发板的资源,用它来驱动蜂鸣器,并通过改变脉宽来改变蜂鸣器发声的频率。下面的程序就是利用PWM来驱动蜂鸣器,脉宽从低到高,再从高到低,周而复始。我们还利用4个LED来指示频率的高低,最高时LED全亮,最低时LED全灭。并且我们用两个按钮来分别暂停蜂鸣器和重新开启蜂鸣器:

#define _ISR_STARTADDRESS 0x33ffff00

#define U32 unsigned int 
typedef unsigned char BOOL;
#define TRUE  1
#define FALSE 0
#define pISR_EINT0  (*(unsigned *)(_ISR_STARTADDRESS+0x20))
#define pISR_EINT1  (*(unsigned *)(_ISR_STARTADDRESS+0x24))

#define rSRCPND  (*(volatile unsigned *)0x4a000000)
#define rINTMSK  (*(volatile unsigned *)0x4a000008)
#define rINTPND  (*(volatile unsigned *)0x4a000010)

#define rGPBCON  (*(volatile unsigned *)0x56000010)
#define rGPBDAT  (*(volatile unsigned *)0x56000014)
#define rGPBUP   (*(volatile unsigned *)0x56000018)

#define rGPFCON  (*(volatile unsigned *)0x56000050)

#define rEXTINT0  (*(volatile unsigned *)0x56000088)

#define rTCFG0  (*(volatile unsigned *)0x51000000)
#define rTCFG1  (*(volatile unsigned *)0x51000004)
#define rTCON  (*(volatile unsigned *)0x51000008)

#define rTCNTB0  (*(volatile unsigned *)0x5100000c)
#define rTCMPB0  (*(volatile unsigned *)0x51000010)

int i,j,x;
BOOL stop;
void delay(U32 x) //延时函数
 {
 for(i=x;i>0;i--)
  for(j=1;j>0;j--);

 }
static  void __irq Key1_ISR(void)//stop key1 中断服务函数
{
 rSRCPND = rSRCPND|(0x1<<1);
 rINTPND = rINTPND|(0x1<<1);
 
 rTCON = rINTPND|(0x1<<1);
 
    rTCON &= ~0x8;
    stop = TRUE;
}

void __irq Key4_ISR(void)//open key
{
 rSRCPND = rSRCPND|0x1;
 rINTPND = rINTPND|0x1;
 
 //rTCON = rINTPND|(0x1<<1);
 
    //rTCON &= ~0x8;
    stop = FALSE;
}


void Main(void)
 {
  int freq;
  rGPBCON = 0x155556;//GPB10-GPB0依次赋值为01 0101 0101 0101 0101 0110 即将GPB0,GPB1设置为TOUT0,TOUT1,GPB5-GPB8为输出,用于点亮LED
  rGPBUP  = 0x7ff; //禁止使能上拉
  rGPFCON = 0xaaaa;//F口为EINT,给按键
  /*按键的一些必要的设置*/
  rSRCPND = 0x07; 
  rINTMSK = ~0x07;
  rEXTINT0 = 0x22;
  
  freq = 2500;
  rTCFG0 &=0xFFFF00;
  rTCFG0 |=0x31;  //prescal为49
  rTCFG1 &=~0xF; // 1/2 ,因为PCLK是50MHZ,故50MHZ/50/2=500KHZ
  rTCNTB0 = 5000;
  rTCMPB0 = freq;
  
  rTCON &= ~0x1F;
  rTCON |= 0xF;//死区无效,自动重载,电平翻转,手动更新,定时器开启,

  rTCON &= ~0x2;//手动更新位清零,PWM开始工作

  pISR_EINT0=(U32)Key4_ISR;
  pISR_EINT1=(U32)Key1_ISR;
  
  stop = FALSE;
  
  rGPBDAT = ~0x60;//两个LED亮,0110 0000 点亮LED3,4
  
  while(1)
  {
   //频率递增
   for(;freq<4950;)
    {
     freq+=50;
     rTCMPB0 = freq;//重新赋值
     delay(1);
   while(stop == TRUE)//stop
     {
      delay(1000);
      if(stop == FALSE) //restart or not
       {
        rTCON &= ~0x1F;
        rTCON |= 0xf;
        rTCON &= ~0x2;//恢复pwm的功能
       
       
       
       }
     }
     
    if(freq == 100)
      rGPBDAT = ~0x1e0; 
    if(freq == 1300)
      rGPBDAT = ~0xe0; 
     
    if(freq == 2500)
      rGPBDAT = ~0x60; 
    if(freq == 3700)
      rGPBDAT = ~0x20; 
    if(freq == 4900)
      rGPBDAT = ~0x0; 
        
    }
    //频率递减
    for(;freq>50;)
    {
     freq -=50;
     rTCMPB0 = freq;
     delay(1);
     while(stop == TRUE)
     {
      delay(1);
      if(stop == FALSE)
      {
       rTCON &= 0x1f;
       rTCON |= 0xf;
       rTCON &= ~0x2;
      
      
      
      }     
     
     
     }
    
    if(freq == 100)
     rGPBDAT = ~0x1e0;
    if(freq == 1300)
     rGPBDAT = ~0xe0;
    if(freq == 2500)
     rGPBDAT = ~0x60;
    if(freq == 3700)
     rGPBDAT = ~0x20;
    if(freq == 4900)
     rGPBDAT = ~0x0;
    
    
    }  
  
  }
  
  
}

实验只要是参考赵春江老师博客做的,在这里感谢赵老师的了

原创粉丝点击