s3c2440中PWM应用

发布时间:2024-07-25  

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)     //Interrupt request status

#define rINTMSK     (*(volatile unsigned *)0x4a000008)      //Interrupt mask control

#define rINTPND     (*(volatile unsigned *)0x4a000010)      //Interrupt request status

 

#define rGPBCON    (*(volatile unsigned *)0x56000010)      //Port B control

#define rGPBDAT    (*(volatile unsigned *)0x56000014)       //Port B data

#define rGPBUP     (*(volatile unsigned *)0x56000018) //Pull-up control B

 

#define rGPFCON    (*(volatile unsigned *)0x56000050)       //Port F control

 

#define rEXTINT0   (*(volatile unsigned *)0x56000088) //External interrupt control register 0

 

#define rTCFG0  (*(volatile unsigned *)0x51000000)      //Timer  configuration

#define rTCFG1  (*(volatile unsigned *)0x51000004)      //Timer  configuration

#define rTCON   (*(volatile unsigned *)0x51000008)      //Timer control

#define rTCNTB0 (*(volatile unsigned *)0x5100000c)       //Timer count buffer 0

#define rTCMPB0 (*(volatile unsigned *)0x51000010)      //Timer compare buffer 0

 

BOOL stop;

 

static void __irq Key1_ISR(void)         //暂停键,关闭蜂鸣器

{

       rSRCPND = rSRCPND | (0x1<<1);

       rINTPND = rINTPND | (0x1<<1);

      

       rTCON &= ~0x8;          //禁止定时器自动重载,即关闭定时器

       stop = TRUE;

}

 

void __irq Key4_ISR(void)          //重启键,开启蜂鸣器

{

       rSRCPND = rSRCPND | 0x1;

       rINTPND = rINTPND | 0x1;

      

       stop = FALSE;

}

 

 

void delay(int a)

{

       int k;

       for(k=0;k
              ;

}

 

void Main(void)

{

       int freq;

      

       rGPBCON = 0x155556;        //B0为TOUT0,B5~B8为输出,给LED

       rGPBUP  = 0x7ff;

       rGPFCON = 0xaaaa;             //F口为EINT,给按钮

       //按钮的一些必要配置

       rSRCPND = 0x07;

       rINTMSK = ~0x07;

       rINTPND =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亮

      

       while(1)

       {

              //频率递增

              for ( ; freq<4950 ; )

              {

                     freq+=10;

                     rTCMPB0 = freq;          //重新赋值

                     delay(20000);

                    

                     while (stop == TRUE)          //暂停

                     {

                            delay(1000);

                            if (stop ==FALSE)        //判断是否重启

                            {

                                   rTCON &= ~0x1F;

                                   rTCON |= 0xf;

                                   rTCON &= ~0x2 ;               //恢复PWM功能

                            }

                     }

                     //4个LED随着频率的高低,时灭时亮

                     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-=10;

                     rTCMPB0 = freq;

                     delay(20000);

                     while (stop == TRUE)

                     {

                            delay(1000);

                            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;

              }

       }    

}

 

这里还需要说明几点:

1、开发板上的蜂鸣器是高电平发声,低电平停止,而TOUT0定时无效时,是高电平输出,因此为了使PWM无效时,蜂鸣器不发声,我把输出电平进行了反转处理(置TCON中的输出反转位);

2、在这里,我是通过按键把stop标志变量置为FALSE来跳出while循环,重新开始蜂鸣,但不知什么原因,如果在while循环内不加一段等待时间,则永远不能跳出循环体,因此我不得不加了一个delay函数,让它等待一段时间。关于这个问题,我还给不出一个满意的解释,也不知是哪里出了问题!

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>