1.什么是PWM
PWM波形是一个周期性波形,周期为T,在每个周期内波形是完全相同的。每个周期内由一个高电平和一个低电平组成。
PWM波形有2个重要参数:一个是周期T,另一个是占空比duty(占空比就是一个周期内高电平的时间除以周期时间的商),高电平时间为Tduty,低电平时间为T(1-duty)。
2..PWM波形的生成原理
先将GPIO引脚电平拉高、同时启动定时器定T*duty时间,时间到了在isr中将电平拉低,然后定时T*(1-duty)后再次启动定时器,然后时间到了后在isr中将电平拉高,然后再定时T*duty时间再次启动定时器····如此循环即可得到周期为T,占空比为duty的PWM波形。
后来因为定时器经常和PWM产生纠结一起,所以设计SoC的时候就直接把定时器和一个GPIO引脚内部绑定起来了,然后在定时器内部给我们设置了PWM产生的机制,可以更方便的利用定时器产生PWM波形。
PWM波形产生有2个寄存器很关键,一个是TCNTB、一个是TCMPB。其中,TCNTB决定了PWM波形的周期,TCMPB决定了PWM波形的占空比。
3.输出电平翻转器
PWM定时器可以规定:当TCNT>TCMPB时为高电平,当TCNT<TCMPB时为低电平。
当电平改变时,TCMPB寄存器中的值就要改,210的PWM定时器帮我们提供了一个友好的工具叫做电平翻转器。
电平翻转器在电路上的实质就是一个电平取反的部件,在编程上反映为一个寄存器位。写0就关闭输出电平反转,写1就开启输出电平反转。
4.蜂鸣器和PWM定时器编程实战
-> 蜂鸣器的工作原理:
蜂鸣器里面有2个金属片,离的很紧但没挨着;没电的时候两个片在弹簧本身张力作用下分开彼此平行;有电的时候两边分别充电,在异性电荷的吸力作用下两个片挨着;
我们只要以快速的频率给蜂鸣器的正负极:供电、断电。进行这样的循环,蜂鸣器的两个弹簧片就会挨着分开挨着分开···形成敲击,发出声音。
-> 硬件原理图:
-> 主要寄存器:
预分频器TCFG0:预分频器的值设置为65,使得分频后等于1MHZ。
分频器TCFG1:分频后的时钟周期为500KHZ。
CON:
TCNTB2、TCMPB2的计算:
我们的时钟频率是500KHz,对应的时钟周期是2us。也就是说每隔2us 计一次数。如果要定的时间是x,则TCNTB中应该写入
rTCNTB2 = 250; // 0.5ms/2us = 500us/2us = 250
rTCMPB2 = 125; // duty = 50%
代码实现:
pwm.c:
#define GPD0CON (0xE02000A0)
#define TCFG0 (0xE2500000)
#define TCFG1 (0xE2500004)
#define CON (0xE2500008)
#define TCNTB2 (0xE2500024)
#define TCMPB2 (0xE2500028)
#define rGPD0CON (*(volatile unsigned int *)GPD0CON)
#define rTCFG0 (*(volatile unsigned int *)TCFG0)
#define rTCFG1 (*(volatile unsigned int *)TCFG1)
#define rCON (*(volatile unsigned int *)CON)
#define rTCNTB2 (*(volatile unsigned int *)TCNTB2)
#define rTCMPB2 (*(volatile unsigned int *)TCMPB2)
// 初始化PWM timer2,使其输出PWM波形:频率是2KHz、duty为50%
void timer2_pwm_init(void)
{
// 设置GPD0_2引脚,将其配置为XpwmTOUT_2
rGPD0CON &= ~(0xf<<8);
rGPD0CON |= (2<<8);
// 设置PWM定时器的一干寄存器,使其工作
rTCFG0 &= ~(0xff<<8);
rTCFG0 |= (65<<8); // prescaler1 = 65, 预分频后频率为1MHz
rTCFG1 &= ~(0x0f<<8);
rTCFG1 |= (1<<8); // MUX2设置为1/2,分频后时钟周期为500KHz
// 时钟设置好,我们的时钟频率是500KHz,对应的时钟周期是2us。也就是说每隔2us
// 计一次数。如果要定的时间是x,则TCNTB中应该写入x/2us
rCON |= (1<<15); // 使能auto-reload,反复定时才能发出PWM波形
//rTCNTB2 = 250; // 0.5ms/2us = 500us/2us = 250
//rTCMPB2 = 125; // duty = 50%
rTCNTB2 = 50;
rTCMPB2 = 25;
// 第一次需要手工将TCNTB中的值刷新到TCNT中去,以后就可以auto-reload了
rCON |= (1<<13); // 打开自动刷新功能
rCON &= ~(1<<13); // 关闭自动刷新功能
rCON |= (1<<12); // 开timer2定时器。要先把其他都设置好才能开定时器
}
main.c:
#include "stdio.h"
void uart_init(void);
void timer2_pwm_init(void);
int main(void)
{
uart_init();
timer2_pwm_init();
while(1)
{
putc('a');
delay();
}
return 0;
}