ARM接口技术——PWM
PWM简介
PWM(Pulse Width Modulation)即脉冲宽度调制,通过对脉冲的宽度进行调制,来获得所需要的波形。
以有源蜂鸣器的控制为例,GPIO加延时可以实现控制,这实际上就是使用GPIO模拟了PWM,但是在延迟中消耗了大量的CPU资源。
使用一个PWM控制器(定时器实现)可以节约CPU资源。
PWM的参数:
- 周期
- 占空比
PWM无源蜂鸣器实验
实验设计和分析
蜂鸣器电路原理图:
无源蜂鸣器BUZZER正极连到了电源,负极连接到一个三极管,我们只需要给三极管输入一个高电平就可以导通蜂鸣器;
三极管的输入端所连接的引脚是GPD0_0,我们需要控制该引脚输出高低变化的电平。
Exynos4412核心板GPD0_0原理图:
GPD0_0可以设置为pwm工作模式,用到的是pwmTOUT0。
芯片手册说明:
它使用了一个时钟源,经过两次分频后再传到PWM定时器,分频原理图:
PWM0和PWM1共用一个一级分频器,PWM2 3 4共用一个一级分频器;
而二级分频器只有一个,PWM0-4共用。
PWM Timer执行过程:
1). 首先需要配置两个寄存器TCNTB和TCMPB的值,它举例是159和109,分别为周期和占空比;
2). 启动定时器;
3). TCNTB会加载到递减计数器,TOUT会输出低电平;
4). 当递减计数器为TCMPB的值时,TOUT会输出高电平,也就是每次递减实际上会对比递减计数器和 TCMPB的值;
5). 当递减计数器减到0时,它会产生中断的请求,不使用它的中断功能,不管;
6). 与此同时递减计数器会重新把TCNTB的值载入(这个需要配置,后面会提到如何配置),意味着会从第二步循环。
需要注意的是以上的描述没有使用到时钟源,时钟源经过分频之后主要是自减计数器使用,它控制了自减的速率,刚才配置的周期再结合自减的速率,共同决定了高低电平变化的快慢。
以实验为例,100MHz经过两次分频后变成1MHz,即自减的频率就是1MHz;周期设置为1000。那么高低电平变化的频率就是1MHz/1000 = 1000Hz。
此外它还提供反向输出的功能,即把原来输出低电平的时间输出高电平,原来输出高电平的输出低电平。
PWM相关寄存器设置:
我们使用的是PWM0,所以主要设置以下寄存器:
1). TCFG0:
PWM0和PWM1的一级分频设置共用TCFG0寄存器,PWM0需要设置低8位,所以TCFG0[7:0] = 。
2). TCFG1:
TCFG1是PWM01234共用的,PWM0需设置低4位:TCFG1[3:0] = 。
3). TCON:
设置PWM0只需要看低5位:
[4]:死区设置,用于高电压控制,这里用不到,不管;
[3]:自动重装载,前面提到一次电平变化,自减计数器会减到0,如果要进行下一个周期的高低电平变化,需要自减计数器需要重装载;
[2]:反向功能,前面提到就是高低电平做了一个反转,这里不用反转;
[1]:更新TCNTB和TCMPB的值,前面提到[3]写1它会自动重装载,但是在第一个周期的时候需要写1手动装载;
[0]:启动PWM。
4). TCNTB和TCMPB:
TCNTB和TCMPB分别设置PWM的周期和高电平占空比,需要注意的是TCNTB必须比TCMPB要大。
5). TCNTO0
这个寄存器就是自减计数器当前的值,这个值只能读。
GPD0CON:
除了PWM配置的寄存器之外,还需要把GPD0_0引脚配置成PWM功能。
GPD0CON[3:0] = 0x2。
PWM实验编程
#include "exynos_4412.h" void PWM0_Init() { /* GPD0_0设为PWM输出功能 */ GPD0.CON = GPD0.CON & (~0xF) | 0x2; /* 一级分频设为100倍 */ PWM.TCFG0 = PWM.TCFG0 & (~0xFF) | 99; /* 二级分频设为1倍 */ PWM.TCFG1 = PWM.TCFG1 & (~0xF); /* 设置PWM0自减计数器 自动重装载 */ PWM.TCON = PWM.TCON | (1<<3); /* 设置PWM0周期1000Hz, 1MHz / 1000Hz = 1000 */ PWM.TCNTB0 = 1000; /* 设置PWM0占空比 */ PWM.TCMPB0 = 600; /* 手动装载一次周期 */ PWM.TCON |= (1<<1); /* 手动关闭更新,因为后续用到自动重装载*/ /* 如果不关闭它可能会一直手动装载,没法自减 */ PWM.TCON &= (~(1<<1)); } void PWM0_start() { /* PWM0开始工作 */ PWM.TCON |= 1; } void PWM0_stop() { /* PWM0停止工作 */ PWM.TCON &= (~1); } void delay(int i) { while(i--); } int main() { PWM0_Init(); while(1) { PWM0_start(); delay(1000000); PWM0_stop(); delay(1000000); } return 0; }
实验现象
蜂鸣器发出滴滴的声音。
END