ARM进阶 005_1 PWM定时器

本文详细介绍了PWM(脉冲宽度调制)的概念、本质和应用,指出PWM是通过定时器来实现的,占空比决定了输出的模拟信号电平。在S3C2440A处理器中,详细阐述了定时器的结构,特别是具有PWM功能的定时器0、1、2和3的工作原理,包括定时计数缓冲寄存器和定时比较缓冲寄存器的作用。此外,还讨论了PWM定时器的操作,包括基本时序、自动重载和双缓冲功能,并通过实例展示了如何初始化和控制PWM定时器来实现音调和音乐播放功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述

       脉冲宽度调制 PWM(Pulse Width Modulation),简称脉宽调制。是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,将模拟信号转换为脉波,一般转换后脉波的周期固定,但脉波的占空比会依模拟信号的大小而改变。广泛应用在从测量、通信到功率控制与变换的许多领域中。

      简而言之,PWM是一种对模拟信号电平进行数字编码的方法。

      实质上是定时器的应用。

 

1、定时器的本质

   定时器的本质是计数器,计数器计的是一个均匀脉冲,即计数固定周期的脉冲(固定频率)  

2、定时器的应用   

    在某个时间,电平翻转,则可以获得一个脉冲,新的频率属性(周期T、脉宽比等)都是由定时器决定。通过这个新的频率,可以控制一些外设。

3PMW

   占空比:就是输出的PWM中,高电平保持的时间 与该PWM的时钟周期的时间之比,即

          脉宽比(占空比) = T(高电平)  /  时钟周期 (若为均匀脉冲,则脉宽比为50%。高电平时间越长,脉宽比越大)

4PMW的应用      

       它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用于测量,通信,功率控制与变换等许多领域。脉冲宽度调制(PWM)是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。

       实际上实现的是能量控制   
应用:调光灯,电机控制.....

 

二、PWM定时器

    1、基本概念

        S3C2440A 516 位定时器。其中定时器0123 具有脉宽调制(PWM)功能。定时器4是一个无输出引脚的内部定时器。定时器0还包含用于大电流驱动的死区发生器。

       定时器 01 共用一个8 位预分频器,定时器234 共用另外的8 位预分频器。每个定时器都有一个可以生成5种不同分频信号(1/21/41/81/16TCLK)的时钟分频器。每个定时器模块从相应8位预分频器得到时钟的时钟分频器中得到其自己的时钟信号。8位预分频器是可编程的,并且按存储在TCFG0TCFG1寄存器中的加载值来分频PCLK

       定时计数缓冲寄存器(TCNTBn包含了一个当使能了定时器时的被加载到递减计数器中的初始值。

       定时比较缓冲寄存器(TCMPBn包含了一个被加载到比较寄存器中的与递减计数器相比较的初始值。这种 TCNTBnTCMPBn的双缓冲特征保证了改变频率和占空比时定时器产生稳定的输出。TCMPBn的值是用于脉宽调制PWM)。当递减计数器的值与定时器控制逻辑中的比较寄存器的值相匹配时定时器控制逻辑改变输出电平。因此,比较寄存器决定PWM输出的开启时间(或关闭时间)。

       简言之,定时计数缓冲寄存器(TCNTBn)决定频率 定时比较缓冲寄存器(TCMPBn)决定周期(何时翻转电平)脉宽比由两者同时决定。

特性:
五个 16 位定时器
两个 8 位预分频器和两个 4 位分频器
可编程输出波形的占空比控制(PWM
自动重载模式或单稳脉冲模式
死区发生

  

   2PWM定时器操作    

           1)基本时序操作

                 

          2)自动重载和双缓冲       

                S3C2440A PWM 定时器包含双缓冲功能,允许在不停止当前定时器操作的情况下为下次定时器操作改变重载值。所以即使设置了新的定时器值,当前定时器操作仍然顺利的被完成。
                定时器值可以被写入到定时器计数缓冲寄存器(TCNTBn)中并且可以从定时器计数监视寄存器(TCNTOn)中读取当前定时器的计数值。如果读取TCNTBn,读出的值不是指示当前计数器的状态而是下次定时器持续时间的重载值。
                自动重载操作在 TCNTn到达0 时复制 TCNTBn TCNTn。写入到TCNTBn的该值,只有在TCNTn到达0并且使能了自动重载时才被加载到TCNTn。如果TCNTn变为0 并且自动重载位为0TCNTn不会进一步任何操作 

 

    实例:使用手动更新位和变相位初始化定时器
         当递减计数器到达 0时发生定时器的自动重载操作。所以必须预先由用户定义一个TCNTn的起始值。在这种情况下,必须通过手动更新位加载起始值。以下步骤描述了如何启动一个定时器:
    1) 初始值写入到 TCNTBnTCMPBn中。
    2) 设置相应定时器的手动更新位。推荐你配制变相开/关位。(无论是否使用变换极性)
    3) 设置相应定时器的开始位来启动定时器(并且清除手动更新位)。
         如果定时器被强制停止,TCNTn保持计数器值并且不会从TCNTBn重载。如果需要设置一个新值,执行手动更新。
  注意:
     无论何时 TOUT变相开/关位改变,是否定时器运行中都将改变TOUT逻辑值。因此,最好带手动更新位配制变相开/关位。


上述图显示了以下过程的结果:
1. 使能自动重载功能。设置 TCNTBn  16050+110)并且设置TCMPBn110。置位手动更新位并且配制变相位 (开/关)。 手动更新位分别设置TCNTnTCMPnTCNTBnTCMPBn的值中。 然后分别设置TCNTBnTCMPBn8040+40)和40,以决定下次重载值。
2. 设置启动位,预设手动更新位为0,变相位为关,自动重载位为开。定时器在定时器分辨率内的等待时间后启动递减计数。
3.  TCNTnTCMPn的值相同时,TOUTn的逻辑电平从低电平变为高电平。
4.  TCNTn到达0时, 发出中断请求并且TCNTBn的值加载到暂存器中。 在下一个定时器标记时刻, 重载TCNTn为暂存器(TCNTBn)的值。
5. 中断服务程序(ISR)中,为下一个持续时间分别设置TCNTBnTCMPBn8020+60)和60
6.  TCNTnTCMPn的值相同时,TOUTn的逻辑电平从低电平变为高电平。
7. 当当 TCNTn到达0时,触发一个中断自动重载TCNTnTCNTBn的值。
8. 中断服务程序(ISR)中,禁止自动重载和中断请求以停止定时器。
9.  TCNTnTCMPn的值相同时,TOUTn的逻辑电平从低电平变为高电平。
10. 尽管 TCNTn到达0,但因为禁止了自动重载,所以TCNTn并不会再次重载并且定时器已经停止了。
11. 不再产生中断请求。

 

 

三、实战演练

1、实现蜂鸣器播放音调的功能

area Init,code, readonly
		code32
		entry
start
		ldr sp, =0x34000000
		import Main
		b Main
stop
		b stop

		end
#include "2440addr.h"

/*
tclk = pclk / ((pre + 1) * div)

fout = 1kHz
pclk = 50MHz
pre = 15
div = 8

1/fout = count * (1/tclk)
1/fout = count * ((pre+1) * div) / pclk

count = pclk / (fout*(pre+1)* div)
	  = 50Mhz / (1KHz * 16 * 8)
	  = 390.625  = 391
*/

/*

*/
#define PCLK 50000000
unsigned int gFre[7] = 
	{500, 650, 880, 1000, 1200, 1400, 1600};
	
void setFre(int fre)
{
	// 定时器配置
	rTCFG0 |= (15<<0);   // pre = 15
	rTCFG1 |= (2<<0);    // div = 8
	
	rTCNTB0 =  PCLK / (fre * 16 * 8); 
	       
	rTCMPB0 = rTCNTB0 >> 1;   // 占空比50%开发板
	
	rTCON |= (1<<3) | (1<<1) | (1<<0);  // 自动加载,手动更新,启动定时器。
	
	rTCON &= ~(1<<1);   //  关闭手动加载
}	


void toutIoCfg(void)
{
	// 引脚配置
	rGPBCON &= ~(3<<0);
	rGPBCON |= (2<<0);   // 配置为tout0功能
}
	
void tout0Init(void)
{

	// 引脚配置
	rGPBCON &= ~(3<<0);
	rGPBCON |= (2<<0);   // 配置为tout0功能
	
	// 定时器配置
	rTCFG0 |= (15<<0);   // pre = 15
	rTCFG1 |= (2<<0);    // div = 8
	
	rTCNTB0 = 391;        
	rTCMPB0 = rTCNTB0 >> 1;   // 占空比50%
	
	rTCON |= (1<<3) | (1<<1) | (1<<0);  // 自动加载,手动更新,启动定时器。
	
	rTCON &= ~(1<<1);   //  关闭手动加载
	 
}

void delay(unsigned int loops)
{
	while(loops-->0);
}

void Main(void)
{

	int i=0;
	toutIoCfg();

	while(1)
	{
	
		for(i=0; i<7; i++)
		{
			setFre(gFre[i]);
			delay(0x10000000);
		}
	}
}

 

2、实现蜂鸣器播放音乐 《大海》 的功能

	AREA Init,CODE,READONLY
	ENTRY
	CODE32
	;IMPORT	Eint_Irq
	import	Main
	EXPORT	__ENTRY
__ENTRY
start
	b		reset_handler
	nop
	b		swi_handler
	nop
	nop
	nop
	b		irq_handler
	nop
reset_handler
	
		;ldr    r0 , =0x53000000
    	;mov    r1 , #0x0
   		;str    r1 , [r0]
   		
		ldr r0,=0x34000000
		mov	sp, r0
		sub r0, r0, #0x500
	
		mrs		r1, cpsr
		bic		r1, r1, #0x1f
		orr		r1, r1, #0x12
		msr		cpsr_c, r1
	
		mov sp, r0
		sub r0, r0, #0x500
	
		mrs		r1, cpsr
		bic 	r1, r1, #0x1f
		bic 	r1, r1, #0x80
		orr 	r1, r1, #0x1f
		msr		cpsr_c, r1
			
		mov		sp, r0
	
	    
    	b		Main
stop	
		b		stop
		
	
irq_handler
		sub		lr, lr, #4
		stmfd	sp!, {r0-r12, lr}
	
		ldr		r1, =0x56000014
		ldr		r0, =0
   		str		r0, [r1]
	
		;bl	Eint_Irq
			
		ldmfd sp!, {r0-r12, pc}^
		
		;import c_swi_handler
swi_handler
		stmfd	sp!, {r0-r12, lr}
		ldr		r0, [lr, #-4]
		bic		r0, r0, #0xff000000
		;bl		c_swi_handler
		ldmfd	sp!, {r0-r12, pc}^
		
	
	export	asmdelay	
asmdelay
		;mov		r7, #0x1000000
		mov		r7, r0
del_loop
		sub		r7, r7, #1
		cmp		r7, #0
		bne		del_loop
		mov		pc, lr
		
	END

#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 rTCFG0  (*(volatile unsigned *)0x51000000)	//Timer 0 configuration
#define rTCFG1  (*(volatile unsigned *)0x51000004)	//Timer 1 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
#define rTCNTO0 (*(volatile unsigned *)0x51000014)	//Timer count observation 0

#define PCLK	50000000

void Buzzer_init(void)
{
	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)/1000;
	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_Freq_Set( unsigned int freq )
{
	
	rTCNTB0 = (PCLK>>7)/freq;
	rTCMPB0 = rTCNTB0>>1;	// 50%
}

void Buzzer_Stop( void )
{
	rGPBCON &= ~3;			//set GPB0 as output
	rGPBCON |= 1;
	rGPBDAT &= ~1;
}

/*
 * 调用该函数, 可以控制蜂鸣器响起, 原理是使能了PWM的TOU1功能为动重装模式
 */
void beep_on( void )
{
	rTCON = (1<<0) | (1<<3);//clear manual update bit
}

/*
 * 调用该函数, 可以控制蜂鸣器响起, 原理是停止了PWM的TOU1功能
 */
void beep_off( void )
{
	rTCON &= ~1;
}


//音阶频率表
const unsigned int yinyue[3][7]
={
	262,  294,  330,  349,  392,  440,  494,  //低音
	523,  587,  659,  698,  784,  880,  988,  //中音
	1046, 1174, 1318, 1396, 1568, 1760, 1975  //高音
};

/*
 * 大海的简谱, 如果大海能够.....
 */
const unsigned char dahai[] =
{
	0x13,  0x15 , 0x16, 0x16, 0x16, 0x16, 0x21, 0x16, 0x15, 0x15, 0x16, 0x15,  //哀愁
	0x13,  0x12 , 0x11, 0x11, 0x11, 0x11, 0x12, 0x13,
	0x13,  0x12 , 0x11, 0x11, 0x11, 0x11, 0x21, 0x16, 0x15, 0x15, 0x16, 0x15,
	0x13,  0x15,  0x16, 0x21, 0x21, 0x16, 0x15, 0x15,//飘远
};

/*
 * 大海的简谱, 控制每一个音的时间 1为长时间 4为短时间
 */
const unsigned char time[] =
{
	4,		4,   4,     2,    4 ,   2,    4,     4,    4,    2,    4,    2 ,//哀愁
	4,      4,   4,     2,    4,    2,    2,     1,
	4,      4,   4,     2,    4,    2,    4,     4,     4,   2,    4,    2,
	4,      4,   2,      4,   2,     4,   4,     1  //飘远

};


//主函数
int Main(void)
{
	unsigned int loop = 0;   //循环控制变量
	unsigned int a = 0;      //计算高中低音使用的变量
	unsigned int b = 0;		 //计算do re mi ....使用的变量
	unsigned int delay = 0;  //计算每一个音延时使用的变量

	//GPH0.GPH0CON = (GPH0.GPH0CON) & ~(0x3f<<1); //初始化key1-key6的引脚功能为输入

	Buzzer_init();   //初始化PWM硬件, 此刻没有启动PWM
	//while( 	( GPH0.GPH0DAT & (0x3ful<<1) )  == (0x3ful<<1) );//等待有任意一个按键被按下,程序才会向下

	while(1)
	{
		//循环播放 音乐
		for(loop=0; loop
   
   
    
    >4;       		    //计算出高低音
			b = (dahai[loop] & 0x0f )-1;  	    //计算出DO RE MI FA SO LA XI
			delay = 0x6500000ul/time[loop];	    //计算出每个音的播放间隔时间

			Buzzer_Freq_Set( yinyue[a][b] ); //重新设定频率
			beep_on();			//发出一个音
			asmdelay(delay);  //播放延时

			beep_off();         //关闭蜂鸣器, 每个音播放完成后有间隔感
			asmdelay(0x20);   //关闭蜂鸣器
		}

		asmdelay(1000000);  //播放延时
	}

}



   
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值