江科大51单片机笔记【15】直流电机驱动(PWM)

server/2025/3/14 0:48:19/

写在前言

  此为博主自学江科大51单片机(B站)的笔记,方便后续重温知识

  在后面的章节中,为了防止篇幅过长和易于查找,我把一个小节分成两部分来发,上章节主要是关于本节课的硬件介绍、电路图、原理图等理论知识,主要是为下章节的代码部分打基础。

  我的单片机是24年12月在tb普中买的,型号是STC89C52,在原视频中引脚或接口不对应的我都会改正,保证在我的机子上能运行才发上来的,还有一些文字部分是我的理解,并非照搬,所以可能有理解不到位的现象。

  如有误或交流,敬请指点提问

本节课一共两个代码,LED呼吸灯和直流电机调速

一、直流电机

1.介绍

  • 直流电机是一种将电能转换为机械能的装置。一般的直流电机有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
  • 直流电机主要由永磁体(定子)、线圈(转子)和换向器组成
  • 除直流电机外,常见的电机还有步进电机(机壳装载了一圈电磁铁,对相对两个电磁铁进行一圈脉冲式通电,优点是严格控速精密)、舵机(跟船舵一样,方便控制转向)、无刷电机(转速特别快)、空心杯电机(体积很小)等
  • 左1就是我们开发板里的电机,有两个电极;左1是N20,一个直流电机+减速箱;右1比左2还多带一个编码器(测速),PID算法(常用)控制电机速度

2.驱动电路

有两种方式

  • 大功率器件直接驱动 

不能正反转上面是一个三极管控制开关,下面是一个蓄流二极管,用于保护电路;因为电机(继电器)是感性负载元件,驱动的时候会出现很高的电压(电感的特效)

这个ULN2003里面也是一个大功率元件

  • H桥驱动

能正反转因为我们使第二、四象限的二极管导通其余的截止,就会令电流向右走,如果反过来就可以令电流向左走;因为没有蓄流二极管保护,所以对mos管和晶体管的耐压特效要求比较高

二、PWM

PWM是控制直流电机调速的


1.介绍

  • PWM(Pulse Width Modulation)即脉冲宽度调制,在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速、开关电源等领域
  • PWM重要参数:
  • 频率=1/Ts  占空比=Ton/Ts  精度=占空比变化步距

三、代码实战

1.LED呼吸灯 

先定义一下引脚

然后再定义一个比较快的延时函数,让这个LED灯先亮一段时间,再灭一段时间,这就是PWM的工作原理,通常为了保证周期是稳定的,不随占空比变化而变化,所以亮和灭加起来的时间也要是个固定值

#include <REGX52.H>sbit LED=P2^0;void Delay(unsigned int t)
{while(t--);
}void main()
{while(1){LED=0;Delay(5);LED=1;Delay(95);}
}

接下来就是让亮和灭的值不断变化,从而实现呼吸灯的效果

我们就可以定义一个变量Time,让亮的时间为Time,灭的时间为100-Time,这样就可以实现两个数加起来是固定值,然后我们再对Time进行for循环,不断++

但是这个速度还是比较快,我们让他每次亮灭变换的时间再久一点,定义一个i,在for循环里嵌套再进行一次for循环,这样就可以实现LED一直从暗到亮

但是我们还想实现从暗到亮,再从亮到暗又怎么做呢,就是把之前的外层对Time的for循环反过来再依次--

#include <REGX52.H>sbit LED=P2^0;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++){LED=0;Delay(Time);LED=1;Delay(100-Time);				}}for(Time=100;Time>0;Time--){for(i=0;i<20;i++){LED=0;Delay(Time);LED=1;Delay(100-Time);				}}}
}

这样就可以实现呼吸灯从暗到亮,再从亮到暗

但是会有一个缺点,就是占用了我们的主循环不断翻转IO口,还比较占用CPU

一般都会写到定时器上,或者从硬件上解决

所以在下面的代码中我们用定时器来实现

2.直流电机调速

在这个代码我们就用定时器来实现LED呼吸灯和直流电机调速功能

产生PWM方法

我们通过改变比较值就可以改变占空比 

由于之前的定时器设置有点慢,我们让他快一点

这个PWM驱动电机在一定范围内是越快越好,一般设置在10K-20K范围里,如果频率比较低,电流就会抖动,如果在1K左右,就会产生一些蜂鸣器的声音

这个中断就是每隔100us中断一次

//timer0.c#include <REGX52.H>/*** @brief  定时器0初始化,100us@12.000MHz* @param  无* @retval 无*/
void Timer0_Init(void)
{TMOD &= 0xF0;		//设置定时器模式TMOD |= 0x01;		//设置定时器模式TL0 = 0xA4;		//设置定时初值TH0 = 0xFF;		//设置定时初值TF0 = 0;		//清除TF0标志TR0 = 1;		//定时器0开始计时ET0=1;EA=1;PT0=0;
}/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{static unsigned int T0Count;TL0 = 0xA4;		//设置定时初值TH0 = 0xFF;		//设置定时初值T0Count++;if(T0Count>=1000){T0Count=0;}
}
*/

由上面的图可以知道要定义两个值,计数值和比较值,

我们让计数值从0-100然后清零,实现定时自增

    Counter++;
    Counter%=100;

这样写和if(counter==100){清零}是一个效果

接下来再让计数值和比较值进行比较

可以观察到如果比较值越大,低电平时间越久,也就是越暗

这样我们通过控制比较值Compare就可以控制LED的亮度,还剩下下了主循环,接下来我们就实现按键功能

#include <REGX52.H>
#include " Delay.h"
#include " Key.h"
#include " Nixie.h"
#include " Timer0.h"sbit LED=P2^0;unsigned char Counter,Compare;void main()
{Timer0_Init();Compare=5;while(1){}}void Timer0_Routine() interrupt 1
{TL0 = 0xA4;		//设置定时初值TH0 = 0xFF;		//设置定时初值Counter++;Counter%=100;if(Counter<Compare){LED=0;}else{LED=1;}
}

注意后面取的Key函数,如果是拿的定时器扫描按键的那章写的Key函数,要在定时器中写上这个Key_Loop()

#include <REGX52.H>
#include " Delay.h"
#include " Key.h"
#include " Nixie.h"
#include " Timer0.h"sbit LED=P2^0;unsigned char Counter,Compare;
unsigned char KeyNum,Speed;void main()
{Timer0_Init();Compare=5;while(1){KeyNum=Key();if(KeyNum==1){Speed++;Speed%=4;}Nixie(1,Speed);}}void Timer0_Routine() interrupt 1
{Key_Loop();TL0 = 0xA4;		//设置定时初值TH0 = 0xFF;		//设置定时初值Counter++;Counter%=100;if(Counter<Compare){LED=0;}else{LED=1;}
}

这样就可以实现按下按键1数码管显示++并且过3就清零

接下来将Speed跟Compare比较值对应起来,就可以实现按键调速并显示

这样就可以实现LED的亮度控制

            if(Speed==0){Compare=5;}
            if(Speed==1){Compare=5;}
            if(Speed==2){Compare=50;}
            if(Speed==3){Compare=100;}    

那我们如果想控制电机,就直接把LED的端口改成电机的端口,而又因为我们的LED是给0亮给1灭,而电机是给1转给0灭,所以需要把极性反过来

这里有个小问题,在刚上电的时候,电机会转动,这是硬件的问题,因为单片机在复位的时候,IO口默认高电平,所以电机会转

#include <REGX52.H>
#include " Delay.h"
#include " Key.h"
#include " Nixie.h"
#include " Timer0.h"sbit Motor=P1^0;unsigned char Counter,Compare;
unsigned char KeyNum,Speed;void main()
{Timer0_Init();Compare=5;while(1){KeyNum=Key();if(KeyNum==1){Speed++;Speed%=4;if(Speed==0){Compare=0;}if(Speed==1){Compare=50;}if(Speed==2){Compare=75;}if(Speed==3){Compare=100;}	}Nixie(1,Speed);}}void Timer0_Routine() interrupt 1
{Key_Loop();TL0 = 0xA4;		//设置定时初值TH0 = 0xFF;		//设置定时初值Counter++;Counter%=100;if(Counter<Compare){Motor=1;}else{Motor=0;}
}

这样就实现本节课的代码啦,按下按键调节电机的速度并且显示出来。


http://www.ppmy.cn/server/174755.html

相关文章

【微服务】Nacos 配置动态刷新(简易版)(附配置)

文章目录 1、实现方法2、配置依赖 yaml3、验证效果 1、实现方法 环境&#xff1a;Nacos、Java、SpringBoot等 主要是在boostrap.yaml中的data-id属性下配置refresh:true来实现动态更新 2、配置依赖 yaml 具体的版本参考官方的说明&#xff1a;官方版本说明 <!--读取boo…

Java常见面试技术点整理讲解——数据库相关(整理中,未完成)

主要整理数据库方面的技术及原理。涉及数据库的事务,范式,索引,结构,视图等问题。 以及比对各个数据库版本之间的差异,存储引擎的差异,最新数据库的使用等。 一:事务 事务是指由一系列数据库操作组成的一个完整的逻辑过程,不可拆分。其中,ACID是DBMS在写入或更新资料…

The 18th Northeast Collegiate Programming Contest H

题目连接 最大值最小&#xff0c;对于套路化的题目来说一般先想二分?(不行试试DP?再试试网络流?),我们先试着二分答案 m i d mid mid&#xff0c;考虑如何检查, 题目说可以选择任一点为根&#xff0c;不好考虑我们不妨先选择1作为根节点 因为要让两个点的距离都尽可能的小,…

Qt C++ 实际开发中宏编译的运用

Qt C 实际开发中宏编译的运用 在Qt C开发中&#xff0c;宏编译&#xff08;Preprocessor Macros&#xff09;是一种强大的工具&#xff0c;用于在编译时根据条件生成不同的代码。宏编译可以用于跨平台开发、调试、功能开关等场景。以下将详细介绍宏编译在Qt C实际开发中的应用…

Linux进程信号二

1.软件条件产生信号 SIGPIPE是一种由软件条件产生的信号&#xff0c;在管道中出现过。 alarm函数 设置一个定时器&#xff0c;当时间到达时&#xff0c;会向进程发送SIGALRM信号。 #include <unistd.h> unsigned int alarm(unsigned int seconds); seconds参数&#x…

golang从入门到做牛马:第十八篇-Go语言递归函数:函数的“自我调用”

在Go语言中,递归是一种强大的编程技术,它允许函数调用自身。递归通常用于解决那些可以通过更小的子问题来解决的问题,例如计算阶乘、生成斐波那契数列等。递归函数在运行过程中会不断调用自身,直到满足某个条件为止。接下来,让我们一起深入了解Go语言中的递归函数。 什么是…

Unity大型游戏开发全流程指南

一、开发流程与核心步骤 1. 项目规划与设计阶段 需求分析 明确游戏类型&#xff08;MMORPG/开放世界/竞技等&#xff09;、核心玩法&#xff08;战斗/建造/社交&#xff09;、目标平台&#xff08;PC/移动/主机&#xff09;示例&#xff1a;MMORPG需规划角色成长树、副本Boss…

计算机视觉算法实战——茶园害虫识别(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ ​​​ 1. 引言 茶园害虫识别是农业领域中的一个重要研究方向&#xff0c;旨在通过计算机视觉技术自动识别茶园中的害虫种类&#xff0c;从…