🚀write in front🚀
🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝💬本系列哔哩哔哩江科大51单片机的视频为主以及自己的总结梳理📚
前言:
本文是根据哔哩哔哩网站上“江协科技51单片机”视频的学习笔记,在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技51单片机教学视频和链接中的内容。
引用:
51单片机-直流电机和PWM-呼吸灯和直流电机调速_单片机20khzpwm调速程序-CSDN博客
51单片机(十五)直流电机驱动(PWM)_直流电机驱动程序-CSDN博客
【嵌入式 · 单片机】一文带你搞懂电机驱动模块_桥式驱动单片机-CSDN博客
51单片机---直流电机(PWM)含源码、小白可入_51单片机_King~30+-GitCode 开源社区
51单片机学习笔记-13直流电机_单片机跟直流电机分别供电-CSDN博客
江协科技 51单片机 直流电机驱动(PWM) 学习笔记_单片机电机驱动电路-CSDN博客
(十五)51单片机——呼吸灯与直流电机调速(PWM)_51单片机pwm控制直流电机-CSDN博客
正文:
0. 🌿概述
在淘宝上购买了江协科技51单片机开发板套件(普中科技STC51单片机A2型号),就上在上一篇博文里说的自己计划学习下江协科技51单片机开发教程,通过STC51单片机这种MCU这种贴近于裸机的开发来增加对于系统硬件层面知识的了解和掌握。
术语和缩略语
缩写 | 全称 | 说明 |
PWM | Pulse Width Modulation | 脉冲宽度调制 |
1. 🚀 直流电机介绍
直流电机介绍:
- 直流电机是一种将电能转换为机械能的装置。一般的直流电机有两个电极,当电极正节的时,电机正传,当电极反接时,电机反转。
- 直流电机主要由永磁体(定子),线圈(动子)和换向器组成
- 除直流电机外,常见的电机还有步进电机,舵机,无刷电机,空心杯电机等。
步进电机的介绍
- 除了直流电机之外还有步进电机,步进电机的结构中,外壳一整圈都分布着磁铁,中间有个转子,转子也是永磁体;通过对不同角度的磁铁进行通电,从而产生不同方向的磁力,改变转子的角度,最终吸引转子朝着特定的方向转动; 其好处就是,转子转动的速度,完全是由我们代码写入的通电时间决定的;
舵机可以输出固定的角度;常用与小车的转向电机;
无刷电机一般在四轴飞行器中会用到;其转速非常快,适合为飞机起飞提供强劲的动力;功率比较大,动力比较足
直流电机是一种功率比较大的负载,直接接在单片机的IO口上肯定是不能直接驱动的而且也可能会损坏单片机的IO口,所以需要再IO和直流电机负载之间加上驱动电路:
常见的驱动电路有两种
- 第一种是大功率器件直接驱动。这种驱动的特点是它驱动电机只能朝一个方向转,因为它不具有调换电机正反方向的功能
- 第二种是H桥驱动,H桥驱动方式是电机驱动里面非常有名的一种驱动方式;这种方式可以控制电机正反转;所以如果我们需要驱动电机正反转,那么就选H桥驱动电路;
- 🌳大功率器件驱动电路,需要通过的电流较大需要功率比较大的器件通常使用达林顿三极管或者MOS三极管,并且电路中需要加一个续流二极管。续流二极管的作用是保护电路的。
- 🌳为什么需要这个续流二极管哪? 因为电机这种负载都是感性负载原件,驱动的时候需要注意它电感的特性,它可能会感应出很大的电压,那为什么会这样哪?
- 🌳这里来分析一下:
当IO控制口输出低电平三极管导通的时候,电流通过直流电机驱动直流电机转动;
当IO控制口输出高电平,驱动电路断开,由于直流电机是感性负载原件具有电感的特性,在电路断开的瞬间电感具有保持电流不能突变的特性,在电路断开的瞬间,电机中中的电感想要保持电流不发生突变,但是电路中的开关已经断开,阻止了电路中电流的位置,电感为了对抗这种阻力就会感应出很大的电压来克服这种阻力来维持电流,最终就是电感感应出的电压和三极管开关之间的对抗,电机在电路断开的瞬间感应出的高电压可能就会击穿三极管开关。- 🌳而如果我们加上了续流二极管,当电极驱动电路断开的瞬间,电机感性器件感应出来的电流就会通过二极管形成回路,从而保护电路中不会感应出高电压。
1.1 ULN2003 驱动电路
ULN2003就是一种常见的功率驱动集成电路器件,它的示意图如上所示:
- 🌳ULN2003在每对输入口1B~7B和输出口1C~7C之间是一个非门,当输入口输入高电平的的时候,输出口输出低电平,例如,当ULN2003 1C口输出低电平时,直流电机的正极接外部的Vcc负极接在ULN2003口的输出口1C上,此时电机就可以转动。
- 🌳ULN2003的输入口输入低电平时,输出口输出高电平,此时ULN2003输出的高电平实际上是没有驱动能力的,它输出的高电平状态输出口能够通过的电流很小。
- 🌳所以ULN2003集成电路器件,主要使用的就是它输入口输入高电平信号时,输出口变为低电平,外接负载的一个电极接在ULN2003的输出口上,负载的另一端接在外部的Vcc上,当ULN2003输出口输出低电平时负载上就有电压产生负载开始工作,当ULN2003输出口输出高电平时负载上没有电压压降产生就停止工作。
1.2H桥驱动
因为整体的形状像是H,所以这个电路被称为H桥;
- 如果使Q1和Q4两个三极管导通,而Q3和Q2两个三极管断开,那么电流就是从左上角流向右下角,如上图所示,这样电机就会朝着一个方向转动;
- 如果反过来,Q3和Q2两个三极管导通,Q1和Q4两个三极管断开,则电流就是从右上角流向左下角,如下图所示,这样电机就会朝着另一个方向转动;
- 通过这种方式控制电机的正反转,因为电流方向既可以向右又可以向左;
因为电机的电流放行既可以往左又可以往右,电机也就没有办法加续流二极管,这就要求我们的晶体管具有很高的耐压特性能够抗的住电机感应出来的高电压,这对器件的要求还是比较高的。
但在实际设计电路中,我们更经常使用集成电路驱动器件,因为这些集成器件是固定的我们可以选择很多的电机驱动芯片,可以自己搜索一下,有了这种芯片之后我们自己就不用自己设计这些复杂的电路,只需要按照芯片手册按照它的要求哪里接电机哪里连输出,这样就可以很轻松的控制电机,如果你对这些电路知识不是很了解的话,那也推荐你去搜索一下电机驱动的芯片,电机驱动的芯片还是非常多的。
2. 🚀PWM介绍
有了电机驱动电路之后,我们在来关心下一个问题,电机调速。如果给电机通上电,电机就会全速运转,如果电机没有通电,电机就会停下来,那么如何让电机保持一个中间的速度哪?这就需要用到PWM(Pulse Width Modulation)进行调速。
什么是PWM:
PWM(脉冲宽度调制),在具有惯性的系统中,可以通过对一系列脉冲进行调制来等效的获得所需要的模拟参量,常应用于电机控速,开关电源等领域。
注意,PWM脉冲宽度调制提到了“在具有惯性的系统中” ,什么事具有惯性的系统哪?例如,电机转动是具有惯性的,当我们短时间断掉电机的电流的时候,电机因为具有惯性它不会立即停还会继续转动,如果我们给电机转1ms停1ms,转1ms停1ms,电机就会因为惯性转动,如果想要电机转动的更快,可以让电机转5ms然后停1ms,转5ms然后停1ms,这样电机就保持一个更快的速度转动。
LED也是具有惯性的系统,当LED点亮之后给LED断电LED灯也不是立马熄灭的它有一个余晖,虽然这个时间不是很长,但它也不是立马熄灭的。
2.1 PWM只适用于惯性系统
PWM只能用于惯性系统,不能用于无惯性系统,那么是什么是无惯性系统哪,例如,用PWM给单片机供电,因为单片机不是惯性系统给单片机断电之后单片机立即就停止运行了。
在PWM波形中有几个重要的参数,分别是:频率、占空比以及精度;
- 🌳频率:是指一个PWM波形周期的倒数;一个周期即为高低电平的总和;
- 🌳占空比:即高电平占据整个周期的比值,占空比越大,高电平时间越长,其转速越高;
- 🌳精度:即占空比变化的步距,占空比变化的步距越小,速度改变的每次就越小,其精度就越高;
频率越高越平滑,如果频率过低可能就会出现电机的抖动。
占空比,一个百分比,占空比越大高电平时间越长转速越快。
精度,就是占空比变化的步距,精度越高每次调节的精度就越高。
通常会保持PWM的一个周期的时间是一样的,当然也可以不用保持每个周期的时间是一样的,但是一般情况下我们习惯保持PWM的一个周期的时间是一样的。
3. 🚀PWM程序电机调速程序
3.1 实验:LED呼吸灯(PWM调节的原理)
第一个实验我们做LED呼吸灯的,LED呼吸灯的实验也就是使用PWD亮度调节的原理,通过调整每个周期内LED点亮的占比来调整LED的亮度,最后实现LED灯亮度不断变化形成呼吸灯的效果。
第一个实验,单独点亮LED灯,在循环里不操作LED灯,LED灯一直点亮,可以看到LED灯的亮度是最亮的。
#include <REGX52.H>
#include <INTRINS.H>
sbit LED=P2^0;
void Delay(unsigned int t)
{
while(t--);
}
void main()
{
unsigned char Time;
unsigned char i;
LED = 0; //点亮LED灯
while(1)
{
}
}
第二个实验,在main函数的While循环里不断的点亮和关闭LED灯,可以看到实验的效果是LED的亮度变暗。
#include <REGX52.H>
#include <INTRINS.H>
sbit LED=P2^0;
void Delay(unsigned int t)
{
while(t--);
}
void main()
{
unsigned char Time;
unsigned char i;
LED = 0; //点亮LED灯
while(1)
{
LED = 0; //点亮LED灯
LED = 1; //关闭LED灯
}
}
第三个对比实验,进一步增大main函数循环中LED灯熄灭的时间,实验的效果是随着LED灯熄灭的时间占比增加,观察实验开发板的结果是LED亮度进一步变暗。
#include <REGX52.H>
#include <INTRINS.H>
sbit LED=P2^0;
void Delay(unsigned int t)
{
while(t--);
}
void main()
{
unsigned char Time;
unsigned char i;
LED = 0; //点亮LED灯
while(1)
{
LED = 0; //点亮LED灯
LED = 1; //关闭LED灯
LED = 1;
LED = 1;
LED = 1;
LED = 1;
LED = 1;
LED = 1;
LED = 1;
LED = 1;
}
}
为了方便控制LED灯点亮和熄灭的时间,我们写了一个‘Delay()’延时函数,使用空循环的方式来控制延时的时间长短。保持每个周期的LED灯点亮和熄灭的总时间相等,我们使用数值'100’来控制LED灯点亮和熄灭的一个周期延时的总时间。最后的LED灯呼吸灯的源码如下:
mani.c
#include <REGX52.H>
#include <INTRINS.H>
sbit LED=P2^0;
void Delay(unsigned int t)
{
while(t--);
}
void main()
{
unsigned char Time;
unsigned char i;
LED = 0;
while(1)
{
for(Time=0; Time<100; Time++){
for(i=0; i<10; i++) //在每个亮度保持一段时间
{
LED = 0;
Delay(Time);
LED = 1;
Delay(100-Time);
}
}
for(Time=100; Time>0; Time--){
for(i=0; i<10; i++) //在每个亮度保持一段时间
{
LED = 0;
Delay(Time);
LED = 1;
Delay(100-Time);
}
}
}
}
在这个实验里我们在程序的主循环里通过"Delay()"延时时间长短的的方法来控制LED亮灭的(模拟PWM),这种方式的优点非常简单,这种方式有缺点也是非常显而易见的,它需要占用我们的主循环来不断的延时来翻转IO口,那在这一段时间内呼吸灯的整个过程中主循环是没法干其他事情的。
通常我们会将PWM给写到定时器里面去,而现在稍微高级一点的单片机,像STC的12系列,15系列都会具有硬件的PWM,或者STM32单片机也会就有硬件的PWM,因为不断反转IO口是一种比较简单比较占用CPU的操作,所以通常我们会用硬件来实现PWM,而且硬件实现通常会寄生到定时器里面去,定时器既可以定时还可以兼具PWM的功能,像一些最新的单片机它的定时器都会有输出PWM的功能。
但是我们的STC89C52是没有的,所以我们就用定时器中断来实现这个功能,如果你了解了单片机定时器中断实现PWM的功能,那以后你再去结束硬件的PWM模块的话你会更加方便的理解。
3.2 实验:PWM直流电机调速
使用定时器中断中的计数器值和比较值进行比较,来控制输出高电平还是低电平来控制PWD(脉冲宽度调制)中高电平的占空比来调节直流电机的转速:
- 🌳在定时器中断里增加计数器的值,并且比较计数器的值和用户设置的比较值,当计数器的值小于用户设置比较值时输出高电平,当计数器的值大于等于比较值时输出低电平。
- 🌳定时器中断计数器的值自增到大于等于100的时候,计数器的值重新开始从0开始计数。这样保证了一个PWM调制的每个周期的时间长度是一样的。
源码如下,主要给出了main.c的源码和timer0.c的源码,其它的源码文件在之前的实验里已经作为模块写出。如果想获得完成的源码,建议现在江协科技提供的示例程序源码。
timer0.c
#include <REGX52.H>
#include "timer0.h"
/**
* @brief 定时器0初始化函数, 100微秒@11.0592MHz
* @param 无
* @retval 无
*/
void Timer0_Init()
{
//AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0xA4; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
//中断部分寄存器
ET0 = 1; //允许定时器T0中断
EA = 1; //允许中断
PT0 = 0; //定时器T0中断优先级
}
/**
* @brief 定时器0中断处理函数模版
* @param 无
* @retval 无
*/
//void Timer0_Routine(void) interrupt 1
//{
// static unsigned int count = 0;
//
// count++;
// //P2_0 = 0;
// if(count >= 500) //定时器T0每1ms中断一次,进入1000次经过了1s
// {
// P2_0 = ~P2_0;
// count = 0;
// }
//
// //定时器溢出之后需要重新装载
// TH0 = (65535 - 1000) / 256; //12MHz晶振,12分频
// TL0 = (65535 - 1000) % 256 + 1; //
//}
main.c
#include <REGX52.H>
#include <INTRINS.H>
#include "time0.h"
#include "Nixie.h"
#include "key.h"
#include "delay.h"
sbit Moto=P1^3;
unsigned char Compare = 0;
unsigned char KeyNum;
void main()
{
unsigned char Speed = 0;
Timer0_Init(); //定时器初始化
Nixie(1,0);
while(1)
{
KeyNum = Key();
if(KeyNum)
{
if(KeyNum == 1)
{
Compare = 50;
Speed = 1;
}
else if(KeyNum == 2)
{
Compare = 75;
Speed = 2;
}
else if(KeyNum == 3)
{
Nixie(1,3);
Compare = 100;
Speed = 3;
}
else if(KeyNum == 4)
{
Compare = 0;
Speed = 0;
}
}
Nixie(1,Speed);
}
}
/**
* @brief 定时器0中断处理函数模版
* @param 无
* @retval 无
*/
void Timer0_Routine(void) interrupt 1
{
static unsigned int count = 0;
count++;
if(count < PWM_Threshold){
Moto = 1; //ULN2003输出低电平,电机转动
}
else{
Moto = 0; //ULN2003输出高电平,电机停止
}
if(count >= 100)
count = 0;
//定时器溢出之后需要重新装载
TH0 = 0xFF; //100微秒@11.0592MHz
TL0 = 0xA4; //定时器装载周期为100微秒
}
4. PWM直流电机调速实验结果
PWM直流电机调速实验结果,如下:
使用逻辑分析仪抓取到的ULN2003输出的PWM波形:
5. 实验问题记录
5.1 问题1:插上直流电机负载并且PWM调速之后单片机运行异常
问题1: 插上直流电机负载并且PWM调速之后单片机运行异常。
在没有插上51单片机之前开发板运行正常,独立按键检测正常,但是在插上直流电机开启PWM调速之后单片机运行异常,独立按键检测异常,LED灯闪烁异常,使用逻辑分析仪抓取了一下波形发现有周期性的高电平产生,为什么会这样哪?现在我的知识能力还不能分析定位原因,我猜测可能是我的PC USB口供电能力不足?(也不应该呀,因为USB直接驱动直流电机旋转不加PWM调速的时候是没问题的,PWM输出占空比的方波调速接上直流电机为什么会造成单片机运行异常哪?)
问题元凶发现:
😭对比江协科技提供的电机调速的源码文件,才发现我写的PWM电机调速源码源码之所以不能正常工作的原因是定时器的溢出周期太长(即PWM的周期):
- 定时器Timer0的定时器溢出周期,也就是PWM调速的周期值写成了 1ms 定时器超时一次,然后每100次定时器超时作为一个PWM周期,这样我写的PWM电机调速程序的PWM周期就变成了 100ms,频率为 1000/100ms=10Hz,这样的PWM频率太低了电机当然就不能正产平稳的运转了,这也就是我刚开始的时候听到直流电机的运转声音不平稳卡顿的原因。
并且因为我的PWM调速频率为10Hz,电机频繁的启停,电机作为感性负载原件它的电感是比较大的当电机频繁启停的时候电机就感应出了很大的感应电压对单片机的运行造成了干扰(造成独立按键被干扰检测异常) - 参考江协科技的电机PWM调速示例源码,其中定时器Timer0的溢出周期为 100us,定时器timer0每溢出100次为一个PWM周期,计算出PWM频率=1000*1000us/(100us*100)=100Hz,这样的PWM调速频率就能够满足直流电机的平稳运行了。
这个错误页进一步的提醒我注意PWM调制的三个关键参数:
- PWM频率
- PWM占空比
- PWM精度(步距)
PWM的频率越高,负载运行的越平稳,如果频率过低可能就会出现电机的抖动。
(并且因为电机的频繁抖动频繁启停,电机作为感性器件感应出来的电压波动还可能会对单片机的执行造成干扰造成按键检测异常(误检测))。
5.2 PWM驱动电机的频率选择
PWM驱动电机的频率在一定范围内越快越好越快越稳定,但也不能过快,过快的的话就会增加开关的损耗(时间)并且也会增加系统资源的负载(中断太多了),驱动电机的时候PWM我们通常设置在10K~20KHz这个范围。
如果这个PWM频率太低的话电机就会抖动,如果在1KHz的时候电机可能会出现鸣叫。但是51单片机的主频比较低,没有办法设置到那么高的频率。