一起玩儿Proteus仿真(C51)——04. 直流电机的启停、加减速和正反转仿真(L298)(二)

news2025/2/28 18:57:48

摘要:本文介绍PWM信号的产生办法和直流电机的启停、加减速和正反转的仿真程序的编写方法

前边已经介绍了2中生成PWM信号的方法了。那么怎样才能节省一下资源,只使用一个定时器呢?这就是介绍的第三种方法,单定时器中断法生成PWM信号,这也是我们这次仿真程序所使用的方法。其基本原理就是通过一个定时器来不停的翻转输出引脚的电平高低来达到输出PWM信号的目的,但是如何控制频率和占空比呢?这就需要不断的改变定时器的初值了。下面先看一下原理图。

这个方法的难点就在于定时器初值的计算和设置。在讲解具体的计算方法之前,首先要明白一个道理,就是定时器的初值是怎样影响定时器中断时间的。定时器并不是初值越大定时器的周期越大,而是恰恰相反,定时器的初值越大,定时器的中断周期越短。这时因为定时器的中断是一种溢出中断,是在初值的基础上在脉冲信号的驱动下不断的累加,直到寄存器发生溢出,才会产生中断。所以,是初值越大,溢出越快,中断的周期也就越小。

明白了这个中断初值与中断周期之间的关系后,就可以来计算中断的初值了,在程序中用INTERVAL变量来表示。首先,要根据需要生成的PWM信号的周期,来计算出来一个PWM信号的初值,也就是定时器使用这个初值为开始,到溢出产生中断,是PWM信号的一个周期。接下来就是将这个周期分成2份,让定时器的一次中断,变成两次,这样就可以产生正确的PWM信号了。

那么,怎么来分割这个周期呢?在这里再定义一个DUTY变量,这个DUTY变量,用来控制输出信号的占空比。定时器是2字节的寄存器,那么其最大值就是0xFFFF,0xFFFF-INTERVAL就是我们之前计算的输出PWM信号周期对应的定时器计时周期的个数,而DUTY是高电平的定时器计时周期的个数,这样,就得到了高电平状态的定时器初值为0xFFFF-DUTY,同样的,0xFFFF-INTERVAL-DUTY就是低电平状态的定时器计时周期的个数,INTERVAL+DUTY就是低电平状态的初值。这样,通过调整DUTY的值,就可以改变PWM信号的占空比了(提示一下:DUTY的值应该在0~0xFFFF-INTERVAL之间)。

下面就是定时器初始化和中断函数的代码,可以对着上面的解释看一下。

sbit ENA = P2^2; // L298使能端

uint DUTY = 200; // PWM输出阈值

uint INTERVAL = 0xFC66; // PWM输出周期

void TIMER1_Init(void) // 定时器初始化

{

TMOD &= 0x0F;

TMOD |= 0x10;

TL1 = (INTERVAL+DUTY);

TH1 = (INTERVAL+DUTY)>>8;

EA = 1;

ET1 = 1;

TR1 = 1;

ENA = 0;

}

void timer1() interrupt 3

{

TR1 = 0;

if( ENA==1 ) {

TL1 = (INTERVAL+DUTY);

TH1 = (INTERVAL+DUTY)>>8;

ENA = 0;

} else {

TL1 = (0xFFFF-DUTY);

TH1 = (0xFFFF-DUTY)>>8;

ENA = 1;

}

TR1 = 1;

}

这就是单定时器中断的PWM信号输出程序。PWM信号除了可以做直流电机调速外,还有很多用途,例如,控制LED灯的闪烁,这个时候与电机调速最大的区别是要求PWM的信号的周期要大得多,可以1秒甚至更长。C51单片机的定时器在12MHz的工作频率下,最大的定时时间也不到0.2s,那么这个时候怎么输出PWM信号呢?

这时候就需要设置一个变量,当每次中断的时候,给这个变量做累加,当累加到一定数值的时候,改变输出端的电平,在累加到一定值的时候,再次改变输出端电平,并将该变量归零,重新开始累加。

下面来看一个简单的例子,假设我要生成一个周期是2秒,占空比是75%的PWM信号。那么我可以设置一个unsigned char类型的变量,该变量值可以在累加过程中在0~255之间不断的循环,那么就将定时器的定时时间设置成:2秒/256=7.8125毫秒。每次中断给这个变量加1。因为占空比是75%,那么可以设置当这个变量值小于256*75%=192时,输出高电平,大于192时,输出高电平。这样就是一个占空比75%的PWM信号了。

具体代码如下所示:

unsigned char PWM = 0;        // 中断计数器

unsigned char DUTY = 192;      // 控制占空比

void timer1() interrupt 3

{

  TR1 = 0;

  TH1 = 0xF0;

  TL1 = 0xBE;

  PWM++;

  P1^0 = PWM<DUTY;

  TR1 = 1;

}

好了,这个仿真程序最难的部分PWM信号输出功能就介绍到这里了。在开发C51程序时,有一个很好的习惯就是把程序中用到的引脚都在程序的开始部分定义成变量,这样的好处是一行的操作都可以通过这个变量来进行,将来万一需要更换使用的引脚,那么只修改这个变量的定义就可以了,而不需要去程序中到处查找哪里用到了这个引脚。

完整的程序代码如下:

#include "reg51.h"

#define uint unsigned int

#define uchar unsigned char

sbit IN1 = P2^0; // L298控制端IN1

sbit IN2 = P2^1; // L298控制端IN2

sbit ENA = P2^2; // L298使能端

sbit KON = P1^0; // 开关

sbit KFAST = P1^1; // 加速

sbit KSLOW = P1^2; // 减速

sbit KDIR = P1^3; // 方向

uint DUTY = 200; // PWM输出阈值

uint INTERVAL = 0xFC66; // PWM输出周期

void TIMER1_Init(void) // 定时器初始化

{

TMOD &= 0x0F;

TMOD |= 0x10;

TL1 = (INTERVAL+DUTY);

TH1 = (INTERVAL+DUTY)>>8;

EA = 1;

ET1 = 1;

TR1 = 1;

ENA = 0;

}

void timer1() interrupt 3

{

TR1 = 0;

if( ENA==1 ) {

TL1 = (INTERVAL+DUTY);

TH1 = (INTERVAL+DUTY)>>8;

ENA = 0;

} else {

TL1 = (0xFFFF-DUTY);

TH1 = (0xFFFF-DUTY)>>8;

ENA = 1;

}

TR1 = 1;

}

void delay(void)

{

uchar i,j;

for(i=0; i<50; i++)

for(j=0; j<20; j++);

}

void main()

{

TIMER1_Init();

IN1 = 0;

IN2 = 0;

while(1)

{

if(KON == 0)

{

IN1 = KDIR;

IN2 = ~KDIR;

} else {

IN1 = 0;

IN2 = 0;

}

if(KFAST == 0)

{

delay();

if(KFAST == 0)

{

while(KFAST==0);

if(DUTY<0xFFFF-INTERVAL-16) DUTY += 16;

}

}

if(KSLOW == 0)

{

delay();

if(KSLOW == 0)

{

while(KSLOW==0);

if(DUTY>15) DUTY -= 16;

}

}

}

}

在这个程序中还有一个需要注意的地方就是按键的处理。对于加减速按键的处理程序,可以说是兼顾了现实中的情况。现实中的机械按键,由于是机械触点,所以在按下的过程中,会存在瞬间的连接不牢固的情况,这样就会出现瞬间的高低电平来回跳动,这个东西有一个专有名词就是按键抖动,所以在使用机械按键的时候,一定要有去抖动措施,可以是硬件的,也可以是软件的。软件的处理方法是,在检测到按键被按下后,不要着急进行处理,要延迟100毫秒左右(不同的设备会略有差异),再进行一次按键状态检测,如果仍然是按下状态,那么说明按键确实按下了。否则,就认为按键没有按下。这样处理就避免了按键抖动过程中将一次按键按下识别为多次的情况。这就是按键中有两个if判断和一个延时的原因。

在仿真中是没有按键抖动和接触不良这个情况的,所以不这样处理也可以。可以去掉其中的延时和一个if语句。

另外在按键的处理中,还有一个while循环语句,这个语句的目的是为了等按键抬起,也就是按键抬起之后,再做响应和处理。这样做的目的是为了避免把一次按键当成多次按键。因为单片机的处理速度很快,这里如果不做等待处理,你会发现你的DUTY值,瞬间就到达边界值了。如果说,你就想把长按当作连续按下,那也要加入一定的延迟,控制一下重复的频率,避免这个按键程序瞬间就被执行很多次,应该是在一种受控的状态下重复才行。

仿真结果如下图所示:

好了,这个直流电机启停、加减速和正反转的仿真实验就介绍到这里了。下个实验再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1441637.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis篇之双写一致性

一、什么的双写一致性 1.定义 双写一致性&#xff1a;当修改了数据库的数据也要同时更新缓存的数据&#xff0c;缓存和数据库的数据要保持一致。 2.正常情况 读操作&#xff1a;缓存命中&#xff0c;直接返回&#xff1b;缓存没命中查询数据库&#xff0c;写入缓存&#xff…

-打印流-

打印流分为字节打印流&#xff1a;PrintStream 字符打印流&#xff1a;PrintWriter特点1&#xff1a;都是只能输出 不能读取 字节打印流&#xff1a; 构造方法&#xff1a;主要用上面的两个构造 成员方法&#xff1a; //创建字节打印流对象&#xff1a;ctrlp注意参数 Prin…

OpenCV 笔记(20):霍夫圆检测

1. 霍夫圆变换 霍夫圆变换(Hough Circle Transform)是一种数字图像处理中的特征提取技术&#xff0c;用于在图像中检测圆形。它将二维图像空间中一个圆转换为该圆半径、圆心横纵坐标所确定的三维参数空间中一个点的过程。因此&#xff0c;圆周上任意三点所确定的圆&#xff0c…

CentOS7集群安装JDK1.8

准备工作 1、提前安装三台虚拟机&#xff0c;可以参考:https://mp.csdn.net/mp_blog/creation/editor/136010108 2、三台虚拟机分别配置免密登录&#xff0c;参考&#xff1a;https://blog.csdn.net/LSW_JAVADP/article/details/121757927 安装 JDK 一、官网下载对应JDK 自…

快速入门Safetensors

快速入门Safetensors 什么是Safetensors架构常用操作速度对比彩蛋 Safetensors官方网址 什么是Safetensors Safetensors是一种新的简单格式&#xff0c;用于安全存储张量(与pickle相反)&#xff0c;而且速度仍然很快(零拷贝)。 架构 常用操作 # pip install safetensors# L…

形态学算法之边界提取的简单python实现——图像处理

原理 图像处理中的边界提取是一项基本而重要的任务&#xff0c;主要用于识别和提取图像中物体的轮廓或边界。 具体流程 1.边缘检测 边界提取的第一步通常是边缘检测。边缘是图像亮度变化显著的地方&#xff0c;是物体与背景或不同物体间的分界线。边缘检测算法通过识别图像中…

最近vscode链接Autodl出现的问题

最近vscode链接Autodl出现的问题 一、问题的概述 在使用vscode连接autodl远程服务器的时候&#xff0c;在vscode的右下角出现了&#xff0c;以下的问题提示&#xff1a; 远程主机可能不符合glibc和libstdc VS Code服务器的先决条件 二、问题的原因 vscode版本过高的问题&…

代码随想录第15题三数之和去重理解

在代码随想录第15题中&#xff0c;对三数之和三元组进行去重时&#xff0c;出现了这样一行代码 nums[i]!nums[i-1] 是为了对和为0的三元组进行去重。 为什么要这样写&#xff0c;为什么敢这样写&#xff0c;下面进行分析。 如上图所示&#xff0c;本题采用了双指针法。i为从…

零基础学Python(9)— 流程控制语句(下)

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。流程控制语句是编程语言中用于控制程序执行流程的语句&#xff0c;本节课就带大家认识下Python语言中常见的流程控制语句&#xff01;~&#x1f308; 目录 &#x1f680;1.while循环 &#x1f680;2.for循环 &#x1…

JAVA设计模式之建造者模式详解

建造者模式 1 建造者模式介绍 建造者模式 (builder pattern), 也被称为生成器模式 , 是一种创建型设计模式. 定义: 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 **建造者模式要解决的问题 ** 建造者模式可以将部件和其组装过程分开…

【GoogleAdmob接入后续】app-ads.txt以及部署Firebase Hosting

目录 一、创建app-ads.txt 二、部署Firebase Hosting云服务&#xff08;仅仅使用它作为一个公开的站点来让Google Admob能访问到app-ads.txt&#xff09; 三、收尾工作 一、创建app-ads.txt 创建txt文件&#xff0c;命名app-ads&#xff0c;后缀txt&#xff0c;内容需去到G…

【QT】day6

#include "home.h" #include "ui_home.h"Home::Home(QWidget *parent): QWidget(parent), ui(new Ui::Home) {ui->setupUi(this);// 从配置文件读取用户名QSettings settings("kim", "ad");username settings.value("usernam…

Python 数据可视化:配色方案

1、引言 在这篇文章中&#xff0c;我们将研究Python的一些配色方案&#xff0c;主要是Seaborn库。这将采用 Python Notebook 格式&#xff0c;其中包括绘图的代码。 2、实验数据 首先导入必要的库&#xff1a; import pandas as pd import seaborn as sns import matplotlib…

腾讯云游戏服务器购买入口,详细配置精准报价

2024年更新腾讯云游戏联机服务器配置价格表&#xff0c;可用于搭建幻兽帕鲁、雾锁王国等游戏服务器&#xff0c;游戏服务器配置可选4核16G12M、8核32G22M、4核32G10M、16核64G35M、4核16G14M等配置&#xff0c;可以选择轻量应用服务器和云服务器CVM内存型MA3或标准型SA2实例&am…

Backtrader 文档学习- Plotting

Backtrader 文档学习- Plotting 虽然回测是一个基于数学计算的自动化过程&#xff0c;还是希望实际通过可视化验证。无论是使用现有算法回测&#xff0c;还是观察数据驱动的指标&#xff08;内置或自定义&#xff09;。 凡事都要有人完成&#xff0c;绘制数据加载、指标、操作…

Vuex介绍和使用

1. 什么是Vuex Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式和库。它解决了在大型 Vue.js 应用程序中共享和管理状态的问题&#xff0c;使得状态管理变得更加简单、可预测和可维护。 在 Vue.js 应用中&#xff0c;组件之间的通信可以通过 props 和事件进行&#xff0c…

微信小程序(三十四)搜索框-带历史记录

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.搜索框基本模板 2.历史记录基本模板 3.细节处理 源码&#xff1a; index.wxml <!-- 1.点击搜索按钮a.非空判断b.历史记录&#xff08;去重&#xff09;c.清空搜索框d.去除前后多余空格2.删除搜索 3.无搜索…

【制作100个unity游戏之24】unity制作一个3D动物AI生态系统游戏3(附项目源码)

最终效果 文章目录 最终效果系列目录前言随着地面法线旋转在地形上随机生成动物不同部位颜色不同最终效果源码完结系列目录 前言 欢迎来到【制作100个Unity游戏】系列!本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第24篇中,我们将探索如何用unity制作一…

肯尼斯·里科《C和指针》第13章 高级指针话题(3)命令行参数

处理命令行参数是指向指针的指针的另一个用武之地。有些操作系统&#xff0c;包括UNIX和MS-DOS&#xff0c;让用户在命令行中编写参数来启动一个程序的执行。这些参数被传递给程序&#xff0c;程序按照它认为合适的任何方式对它们进行处理。 13.4.1 传递命令行参数 这些参数如何…

BestEdrOfTheMarket:一个针对AVEDR绕过的训练学习环境

关于BestEdrOfTheMarket BestEdrOfTheMarket是一个针对AV/EDR绕过的训练学习环境&#xff0c;广大研究人员和信息安全爱好者可以使用该项目研究和学习跟AV和EDR绕过相关的技术知识。 支持绕过的防御技术 1、多层API钩子&#xff1b; 2、SSH钩子&#xff1b; 3、IAT钩子&#x…