参考教程:[15-1] 直流电机驱动(PWM)_哔哩哔哩_bilibili
1、直流电机是一种将电能转换为机械能的装置。一般的直流电机有两个电极,当电极正接时,电机正转,当电极反接时,电机反转。直流电机主要由永磁体(定子)、线圈(转子)和换向器组成。除直流电机外,常见的电机还有步进电机、舵机、无刷电机、空心杯电机等。
2、电机驱动电路:
(1)大功率器件直接驱动:(D1二极管用于保护电路,否则断电后,由于电机有电感的特性,但是电路却是断路,因此电机两端的电压会异常地高;电机只能单向转动)
(2)H桥驱动:(电机可以正向转动,也可以逆向转动,要求三极管有很强的耐压特性)
(3)开发板上的电机模块:VCC和OUT1接电机的两端,当然,电机一端接VCC另一端可以接OUT1-4,下面代码以接OUT1为准,OUT1的电平与IN1的电平相同,也就是与P10有关,且OUT1高电平才能驱动电机转动(这点可以看原理图中是否有非门,或者说看有没有取反,一般引脚有取反的都是对应低电平驱动,没有取反的都是高电平驱动)。
3、PWM:PWM(Pulse Width Modulation)即脉冲宽度调制,在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速、开关电源等领域。
4、产生PWM方法:
(1)模型结构:
(2)波形:
5、LED呼吸灯:
(1)理论上给LED灯一个模拟信号(如上面那个紫色虚线的波形),LED灯的亮度可以从低慢慢变高,再从高慢慢变低,但是这在单片机中难以实现(因为软件中只能写出数字信号),不过可以给LED灯一个占空比不断变化的方波脉冲(可能不是专业名词,能理解就行),因为有PWM调制,就相当于给了LED一个模拟信号量。
(2)将下列代码添加到main.c文件中,然后进行编译、调试。
#include <REGX52.H>
sbit LED = P2^0; //第一盏LED灯
void Delay(unsigned int t) //简陋的延时函数
{
while(t--);
}
void main()
{
unsigned char Time, i;
while(1)
{
for(Time=0;Time<100;Time++) //亮度增加(通过改变占空比实现)
{
for(i=0;i<20;i++) //20个相同脉宽的脉冲
{
LED=0;
Delay(Time); //低电平占时Time(亮度逐渐变大)
LED=1;
Delay(100 - Time); //高电平占时100 - Time(暗度逐渐变小)
}
}
//亮度达到巅峰
for(Time=100;Time>0;Time--) //亮度下降(通过改变占空比实现)
{
for(i=0;i<20;i++) //20个相同脉宽的脉冲
{
LED=0;
Delay(Time); //低电平占时Time(亮度逐渐变小)
LED=1;
Delay(100 - Time); //高电平占时100 - Time(暗度逐渐变大)
}
}
//LED完全熄灭
}
}
6、按键调节直流电机转速:
(1)为了调节直流电机转速,在下面的代码中会引入变量Compare,这是用户自设的一个比较值,当计数器低于比较值时输出高电平、高于比较值时输出低电平,通过更改比较值产生占空比不同的脉冲,以此达到调节直流电机转速的目的。(直流电机由方波信号驱动,高电平时直流电机工作,低电平时会由于惯性继续运动,不过运动速度逐渐下降)
(2)将直流电机导线的一端插到开发板步进电机模块的U5脚,另一端插到U5隔壁的1号脚,然后在项目中添加下列文件,接着进行编译,按照main.c文件中的注释进行调试。(独立按键和数码管可以不用定时器扫描,我的项目中独立按键是用定时器扫描,数码管则不是,所以不用延时函数,另外数码管只需要静态显示即可,所以不需要消影)
①Timer0.c文件:
#include <REGX52.H>
/**
* @brief 定时器0初始化,100us@11.0592MHz
* @param 无
* @retval 无
*/
void Timer0_Init()
{
TMOD = 0x01;
//仅关注0-3位,0位和1位决定配置定时器0为模式1(16位定时器/计数器)
//2位决定选择定时器功能(与之相对的是计数器功能)
//3位决定打开定时器(具体见原理图以及手册)
//TMOD“不可位寻址”,也就是说对其进行配置的话不能按位进行配置,要“一气呵成”
TF0 = 0; //中断标志位初始化为0,计数产生溢出时该位被置为1,向CPU发出中断请求,CPU响应中断后该位被置为0
TR0 = 1; //允许定时器T0计数
//由于TMOD的3位,也就是GATE被置为了0,那么无论IE0、IT0如何配置,结果都是一样的
TL0 = 0xA4; //设置定时初值
TH0 = 0xFF; //设置定时初值
ET0 = 1;
EA = 1;
PT0 = 0;
//结合原理图理解,这是在配置中断系统,创造产生中断的环境
}
/* 定时器0函数中断模板
void Timer0_Routine() interrupt 1 //CPU响应中断后执行的函数(每隔100us执行一次)
{
static unsigned int T0Count = 0; //定义计数器
T0Count++;
if(T0Count >= 10000) //每10000个中断信号(1秒)执行一次下面的代码段
{
T0Count = 0;
}
//每次中断结束都要重置计数单元
TL0 = 0xA4; //设置定时初值
TH0 = 0xFF; //设置定时初值
}
*/
②main.c文件:
#include <REGX52.H>
#include "Nixie.h"
#include "Timer0.h"
#include "key.h"
sbit Motor = P1^0; //电机(是P1^0还是P2^0,具体看开发板的原理图)
unsigned char Counter, Compare;
unsigned char KeyNum;
unsigned char Speed;
void main()
{
Timer0_Init();
Compare = 0;
while(1)
{
KeyNum = Key();
if(KeyNum == 1) //每按一次按键1,转速提高一级
{
Speed++; //转速等级分为4级
Speed %= 4; //超过3级,转速回归0级
if(Speed == 0){Compare = 0;} //不转
if(Speed == 1){Compare = 5;} //慢转
if(Speed == 2){Compare = 50;} //稍快转
if(Speed == 3){Compare = 100;}//快转
}
Nixie(2,Speed); //第二个数码管显示当前档位
}
}
void Timer0_Routine() interrupt 1 //CPU响应中断后执行的函数
{
static unsigned int T0Count = 0; //定义计数器
T0Count++;
if(T0Count >= 200) //每200个中断信号(20ms)执行一次下面的代码段(20ms正好是按键消抖需要的时间)
{
Key_Loop(); //定时器扫描独立按键
T0Count = 0;
}
Counter++;
Counter %= 100;
if(Counter < Compare) //计数器和自设比较值比较
{
Motor = 1; //计数器低于比较值,输出高电平
}
else
{
Motor = 0; //计数器高于比较值,输出低电平
}
//每次中断结束都要重置计数单元
TL0 = 0xA4; //设置定时初值
TH0 = 0xFF; //设置定时初值
}