草庐IT

【51单片机】直流电机的驱动和PWM调速

Collin Lee 2023-10-09 原文

51单片机驱动直流电机与 PWM 调速是通过使用 51 单片机来控制直流电机的转速和方向。51 单片机通过控制电机的电流来实现驱动,并通过生成 PWM 信号来调节电机的转速。使用 PWM 调速可以使得直流电机的转速精确可控,并且减少了电机的功率损耗。在 51 单片机的控制系统中,这两种技术都是常见的应用。

一般的直流电机有两个电极,当电极正接时,电机正转,当电极反接时,电机反转。除直流电机外,常见的电机还有步进电机、舵机、无刷电机、空心杯电机等。

一、硬件电路

电机属于大功率负载,如果直接接在i/o口,会损坏单片机硬件。因此需要在单片机和电机之间加入驱动电路,常见的是直接驱动和h桥驱动。

直接驱动

H桥驱动

这里我们使用单片机开发板自带的资源ULN2002D进行驱动。

ULN2003D 是一种高电流可溶性继电器驱动芯片,它通常用于驱动直流电机。ULN2003D 的主要作用是将较低电压的控制信号转换为较高电压的驱动信号,从而可以通过驱动直流电机。ULN2003D 具有高电流输出能力,使用它可以减少控制系统的电路复杂度和功率损耗,并且可以额外提供驱动电流。ULN2003D 在驱动直流电机时主要用于将低电压的控制信号转换为高电压的驱动信号,从而提高驱动能力,减少电路复杂度和功率损耗。

他的内部结构与直接驱动的电路基本相同。内置达林顿管和续流二极管,工作电压高,工作电流大,灌电流可达500mA,并且能够在关态时承受50V 的电压,输出还可以在高负载电流并行运行。

这里我们使用P1.0控制OUT1来间接控制电机。

二、PWM调速

PWM 调速是一种用来控制直流电机转速的方法。它的原理是通过不断地开关直流电源来调节电机的转速。

PWM 调速的具体实现方法是通过生成一个频率固定的、占空比可变的 PWM 信号来控制直流电机的转速。占空比是指 PWM 信号在一个周期内的工作时间与总时间的比值。如果占空比越高,电机的转速就越快;如果占空比越低,电机的转速就越慢。PWM 调速的优点在于可以精确控制电机的转速,并且还可以减少电机的功率损耗。

重要参数:

频率 = 1 / TS 占空比= TON / TS 精度= 占空比变化步距

这里我们使用单片机内部的资源,定时器进行pwm控制。

首先配置定时器,使用51单片机小工具快速生成定时器初始化函数。

void Timer0Init(void)        //10毫秒@11.0592MHz
{
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x01;        //设置定时器模式
    TL0 = 0xA4;        //设置定时初值
    TH0 = 0xFF;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0=1;        //中断初始化【程序生成的函数内没有这个】
    EA=1;                    //中断初始化
    PT0=0;                //中断初始化
}

在主函数中添加定时器的中断函数。我们提前定义一个比较值Compare用来与计数值比较,这个值会响应按键的动作进行加减,从而实现按键控制速度。

计数器内设置count定时自增,当count<compare时将P1.0口(Motor)置1,反之置0使其停止运行。当这一过程执行的速度很快,就会产生pwm方波。

void Timer0Routine() interrupt 1        //中断
{
    TL0 = 0xA4;        //设置定时初值
    TH0 = 0xFF;        //设置定时初值
    Count++;                                        //每100us打断一次,计数器加1
    if(Count<Compare)                            
    {
        Motor=1;
    }
    else
    {
        Motor=0;
    }
    Count%=100;
}

三、按键选择

这里我们使用单片机开发板上的独立按键模块。原理图如下。

我们需要设定程序,对独立按键的四个i/o口进行扫描,实时读取按键信息,将按下的按键值传回函数。

unsigned char Key()
{
    unsigned char KeyNumber=0;
    if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
    if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
    if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
    if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
    return KeyNumber;
}

在主函数中我们要将传回的按键值与他对应的转速进行绑定。为了方便,我们只使用按键1对电机转速进行控制。

        KeyNum=Key();
        if(KeyNum==1)
        {
            Speed++;
            Speed%=5;        //当speed超过4将其值0,从而实现转速选择的循环
            if(Speed==0){Compare=0;}
            if(Speed==1){Compare=50;}
            if(Speed==2){Compare=70;}
            if(Speed==3){Compare=90;}
            if(Speed==4){Compare=100;}
        }

为了方便查看当前转速等级,我们引入数码管来显示转速等级。

数码管驱动文件如下。

#include <REGX52.H>
#include "Delay.h"

unsigned char smgText[]={0x3F,0x06,0x5B,0x4f,0x66,0x6D,0x7D,0x07,0x7F,0x6F};      

void smg(unsigned char location,number)
{
    switch(location)                                                              //location显示的数字
        {
            case 1:P2_4=1;P2_3=1;P2_2=1;break;
            case 2:P2_4=1;P2_3=1;P2_2=0;break;
            case 3:P2_4=1;P2_3=0;P2_2=1;break;
            case 4:P2_4=1;P2_3=0;P2_2=0;break;
            case 5:P2_4=0;P2_3=1;P2_2=1;break;
            case 6:P2_4=0;P2_3=1;P2_2=0;break;
            case 7:P2_4=0;P2_3=0;P2_2=1;break;
            case 8:P2_4=0;P2_3=0;P2_2=0;break;
        }
        P0=smgText[number];        //number显示的数字
        Delay(1);
        P0=0x00;
    }

在主函数中我们只需要输入smg(1,Speed);即可将速度数字显示在数码管第一位。

四、程序总览

本次项目为了使程序调理情绪便于理解,我使用了模块化编程。将按键程序、延迟程序、数码管程序、定时器程序分别进行封装。

主函数完整内容如下:

#include <REGX52.H>
#include "Key.h"
#include "Delay.h"
#include "smg.h"
#include "Timer0.h"

sbit Motor=P1^0;
unsigned char KeyNum,Speed;
unsigned char Compare,Count;

void main()
{
    Motor=0;
    Timer0Init();
    
    for(;;)
    {
        KeyNum=Key();
        if(KeyNum==1)
        {
            Speed++;
            Speed%=5;
            if(Speed==0){Compare=0;}
            if(Speed==1){Compare=50;}
            if(Speed==2){Compare=70;}
            if(Speed==3){Compare=90;}
            if(Speed==4){Compare=100;}
        }
        smg(1,Speed);
    }
}


void Timer0Routine() interrupt 1        //中断
{
    TL0 = 0xA4;        //设置定时初值
    TH0 = 0xFF;        //设置定时初值
    Count++;                                        //每100us打断一次,计数器加1
    if(Count<Compare)                           
    {
        Motor=1;
    }
    else
    {
        Motor=0;
    }
    Count%=100;
}

独立按键程序如下:

#include <REGX52.H>
#include "Delay.h"


unsigned char Key()
{
    unsigned char KeyNumber=0;
    if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
    if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
    if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
    if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
    return KeyNumber;
}

延迟函数如下:

#include <intrins.h>

void Delay(unsigned int n)

{
    unsigned int i,j;
    for(j=n;j>0;j--)
    for(i=112;i>0;i--);
}

数码管程序如下:

#include <REGX52.H>
#include "Delay.h"

unsigned char smgText[]={0x3F,0x06,0x5B,0x4f,0x66,0x6D,0x7D,0x07,0x7F,0x6F};      

void smg(unsigned char location,number)
{
    switch(location)                                                              //location显示的数字
        {
            case 1:P2_4=1;P2_3=1;P2_2=1;break;
            case 2:P2_4=1;P2_3=1;P2_2=0;break;
            case 3:P2_4=1;P2_3=0;P2_2=1;break;
            case 4:P2_4=1;P2_3=0;P2_2=0;break;
            case 5:P2_4=0;P2_3=1;P2_2=1;break;
            case 6:P2_4=0;P2_3=1;P2_2=0;break;
            case 7:P2_4=0;P2_3=0;P2_2=1;break;
            case 8:P2_4=0;P2_3=0;P2_2=0;break;
        }
        P0=smgText[number];        //number显示的数字
        Delay(1);
        P0=0x00;
    }

定时器程序如下:

#include <REGX52.H>
void Timer0Init(void)        //10毫秒@11.0592MHz
{
    TMOD &= 0xF0;        //设置定时器模式
    TMOD |= 0x01;        //设置定时器模式
    TL0 = 0xA4;        //设置定时初值
    TH0 = 0xFF;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0=1;        //中断初始化【程序生成的函数内没有这个】
    EA=1;                    //中断初始化
    PT0=0;                //中断初始化
}


//主函数中断示例
//void Timer0Routine() interrupt 1        //中断
//{
//    TL0 = 0x00;                                                //设置定时初值,让定时器重新开始计时            
//    TH0 = 0xDC;                                                //设置定时初值【注意:这里的定时初值要与函数Timer0Init内的初值一样】
//    Timer0Count++;                                        //每10ms打断一次,计数器加1
//    if(Timer0Count>=100)                            //当计数器到100,即经过1秒后让秒加一,然后时分做出响应变化
//    {
//                
//    
//    }

每个程序文件单独存放在c文件中,并存在与其同名的h头文件将函数定义在其中。

以定时器函数为例,他的头函数将这样定义!!

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init(void);

#endif

本文为学习笔记,欢迎大家在评论区友善讨论,指出我的不足!

有关【51单片机】直流电机的驱动和PWM调速的更多相关文章

  1. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  2. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  3. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  4. ruby - 运行测试时静音 Chrome 驱动程序控制台输出 - 2

    我使用的是最新版本的Chrome(32.0.1700.107)和Chrome驱动程序(V2.8)。但是当我在Ruby中使用以下代码运行示例测试时:require'selenium-webdriver'WAIT=Selenium::WebDriver::Wait.new(timeout:100)$driver=Selenium::WebDriver.for:chrome$driver.manage.window.maximize$driver.navigate.to'https://www.google.co.in'defapps_hoverele_hover=$driver.find_

  5. node.js - 从未编写过任何自动化测试,我应该如何开始行为驱动开发? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。多年来,我一直在使用多种语言进行编程,并且认为自己总体上相当擅长。但是,我从未编写过任何自动化测试:没有单元测试,没有TDD,没有BDD,什么都没有。我已经尝试开始为我的项目编写适当的测试套件。我可以看到在进行任何更改后能够自动测试项目中所有代码的理论值(value)。我可以看到像RSpec和Mocha这样的测试框架应该如何使设置和运行所述测试变得相当容易

  6. ruby-on-rails - 在 Rails/Capybara/Poltergeist 规范中使用 url_for 将驱动程序发送到 example.com 而不是应用程序 - 2

    如果我在功能规范中调用url_for,它会返回一个以http://www.example.com/开头的绝对URL.Capybara会很乐意尝试加载该站点上的页面,但这与我的应用程序无关。以下是重现该问题的最少步骤:从这个Gemfile开始:source'https://rubygems.org'gem"sqlite3"gem"jquery-rails"gem"draper"gem"rails",'4.1.0'gem"therubyracer"gem"uglifier"gem"rspec-rails"gem"capybara"gem"poltergeist"gem"launchy"运行

  7. 驱动开发:内核无痕隐藏自身分析 - 2

    在笔者前面有一篇文章《驱动开发:断链隐藏驱动程序自身》通过摘除驱动的链表实现了断链隐藏自身的目的,但此方法恢复时会触发PG会蓝屏,偶然间在网上找到了一个作者介绍的一种方法,觉得有必要详细分析一下他是如何实现的进程隐藏的,总体来说作者的思路是最终寻找到MiProcessLoaderEntry的入口地址,该函数的作用是将驱动信息加入链表和移除链表,运用这个函数即可动态处理驱动的添加和移除问题。MiProcessLoaderEntry(pDriverObject->DriverSection,1)添加MiProcessLoaderEntry(pDriverObject->DriverSection,

  8. micropython复现经典单片机项目(二)可视化音频 频谱解析(基本搞定) - 2

    本人是音乐爱好者,从小就特别喜欢那个随着音乐跳动的方框效果,就是这个:arduino上一大把对,我忍你很久了,我就想用mpy做,全网没有,行我自己研究。果然兴趣是最好的老师,我之前有篇博客专门讲音频,有兴趣的可以回顾一下。提到可视化频谱,必然绕不开fft,大学学过这玩意,当时一心玩,老师讲的一个字都么听进去,网上教程简略扫了一下,大该就是把时域转频域的工具,我大mpy居然没有fft函数,奶奶的,先放着。音频信息如何收集?第一种傻瓜式的ADC,模拟转数字,原始粗暴,第二种,I2S库,我之前博客有讲过,数据是PCM编码。然后又去学PCM编码,一学豁然开朗,舒服,以代码为例:audio_in=I2S

  9. ruby - 事件/观察者驱动的 Ruby on Rails - 2

    我有一个适用于事件/监听器模型的应用程序。发布了几种不同类型的数据(事件),然后许多不同的事情可能需要也可能不需要对该数据(监听器)采取行动。监听器的发生没有特定的顺序,每个监听器将决定是否需要对事件采取行动。Rails应用程序有哪些工具可以完成此任务?我希望自己不必这样做(尽管我可以。这没什么大不了的。)编辑:观察者模式可能是更好的选择 最佳答案 查看EventMachine.它是一个非常流行的Ruby事件处理库。它看起来相当不错,而且很多其他库似乎都在利用它(Cramp)。这是一个很好的介绍:http://rubylearnin

  10. 51单片机——74HC595的应用(SPI实践) - 2

    目录SPI总线SPI总线概述 SPI总线分类SPI优点及缺点SPI接口硬件原理SPI四种工作模式 74HC595应用74HC595芯片概述74HC595封装及管脚功能74HC595工作原理 ​编辑 74HC595串行转并行点亮LED灯 程序实现  Proteus运行结构示意图SPI总线SPI总线概述 SPI(SerialPeripheralinterface):串行外围设备接口 用途:用来在微控制器与外围设备芯片之间实现数据交换 特点:高速、全双工、同步 SPI总线分类四线制全双工SPI(同时收发)MISO    主机输入/从机输出MOSI    主机输出/从机输入SCLK   串行时钟CS或

随机推荐