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

摘要:本文介绍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://xiahunao.cn/news/2775709.html

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

相关文章

多线程JUC:等待唤醒机制(生产者消费者模式)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;多线程&JUC&#xff1a;解决线程安全问题——synchronized同步代码块、Lock锁 &#x1f4da;订阅专栏&#xff1a;多线程&am…

伯克利研究院推出Ghostbuster用于检测由LLM代笔的文本

Ghostbuster的架构&#xff0c;用于检测人工智能生成文本的最先进的新方法 像 ChatGPT 这样的大型语言模型写得非常好&#xff0c;但事实上&#xff0c;它们已经成为一个棘手的问题。学生们已经开始使用这些模型代写作业&#xff0c;导致一些学校禁止 ChatGPT。此外&#xff0c…

RocketMQ客户端实现多种功能

目录 RocketMQ客户端基本流程 消息确认机制 1、消息生产端采用消息确认加多次重试的机制保证消息正常发送到RocketMQ 单向发送 同步发送 异步发送 2、消息消费者端采用状态确认机制保证消费者一定能正常处理对应的消息 3、消费者也可以自行指定起始消费位点 广播消息 …

在Visual Studio中引用和链接OpenSceneGraph (OSG) 库

在Visual Studio中引用和链接OpenSceneGraph (OSG) 库&#xff0c;按照以下步骤操作&#xff1a; 构建或安装OSG库 下载OpenSceneGraph源代码&#xff08;如3.0版本&#xff09;并解压。使用CMake配置项目&#xff0c;为Visual Studio生成解决方案文件。通常您需要设置CMake中的…

UE4运用C++和框架开发坦克大战教程笔记(十八)(第55~57集)

UE4运用C和框架开发坦克大战教程笔记&#xff08;十八&#xff09;&#xff08;第55~57集&#xff09; 55. UI 进入退出动画HideOther 面板出现时隐藏其他面板添加面板出现和收起的动画效果编写遮罩管理器前的准备 56. 弹窗进入界面57. UI 显示隐藏与遮罩转移完善遮罩管理器 55…

包装效果图为何要用云渲染100?渲染100邀请码1a12

包装效果图能吸引用户注意力&#xff0c;提升销量&#xff0c;随着技术的发展&#xff0c;越来越多的设计师开始使用云渲染来处理效果图&#xff0c;云渲染有什么优势呢&#xff1f;以渲染100为例我来说下。 1、节省时间和成本 渲染100拥有超过10万台的高性能渲染节点&#x…

疑似针对安全研究人员的窃密与勒索

前言 笔者在某国外开源样本沙箱平台闲逛的时候&#xff0c;发现了一个有趣的样本&#xff0c;该样本伪装成安全研究人员经常使用的某个渗透测试工具的破解版压缩包&#xff0c;对安全研究人员进行窃密与勒索双重攻击&#xff0c;这种双重攻击的方式也是勒索病毒黑客组织常用的…

关节点检测

https://www.bilibili.com/video/BV19g4y1777q/?p2&spm_id_frompageDriver 关节点检测全流程 YOLO:单阶段&#xff0c;快&#xff1b; MMPose&#xff1a;双阶段&#xff0c;准&#xff1b; 标注工具Labelme 用Labelme标注样本数据集

停车场|基于Springboot的停车场管理系统设计与实现(源码+数据库+文档)

停车场管理系统目录 目录 基于Springboot的停车场管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员功能实现 &#xff08;1&#xff09;车位管理 &#xff08;2&#xff09;车位预订管理 &#xff08;3&#xff09;公告管理 &#xff08;4&#…

AVR 328pb ADC基本介绍和使用

AVR 328pb ADC基本介绍和使用 &#x1f4cd;结合参考同架构lgt8f328p中文文档&#xff1a;http://www.prodesign.com.cn/wp-content/uploads/2023/03/LGT8FX8P_databook_v1.0.4.pdf &#x1f4d8;328pb ADC特性 • 10-bit Resolution 10位分辨率 • 0.5 LSB Integral Non-lin…

Java stream 流的基本使用

Java stream 的基本使用 package com.zhong.streamdemo.usestreamdemo;import jdk.jfr.DataAmount; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.util.ArrayList; import java.util.Comparator; import java.util.Li…

LabVIEW网络测控系统

LabVIEW网络测控系统 介绍了基于LabVIEW的网络测控系统的开发与应用&#xff0c;通过网络技术实现了远程的数据采集、监控和控制。系统采用LabVIEW软件与网络通信技术相结合&#xff0c;提高了系统的灵活性和扩展性&#xff0c;适用于各种工业和科研领域的远程测控需求。 随着…

基于微信小程序的新生报到系统的研究与实现,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【Linux】Shell编程

Shell编程 目录 Shell编程1.shell基础1.输入重定向 & 输出重定向2.管道3.特殊字符(3.1)通配符(3.2)引号(3.3)注释符(#) 4.别名5.命令历史history 2.Shell脚本Shell脚本的执行方式(1)为脚本文件加上可执行权限,然后在命令行直接输入shell脚本文件名执行。(2)sh shell脚本名(…

基于Robei EDA--实现串口通信

一、串口简介 串口作为常用的三大低速总线&#xff08;UART、SPI、IIC&#xff09;之一&#xff0c;在设计众多通信接口和调试时占有重要地位。但UART和SPI、IIC不同的是&#xff0c;它是异步通信接口&#xff0c;异步通信中的接收方并不知道数据什么时候会到达&#xff0c;所…

深度优先搜索(DFS)与广度优先搜索(BFS):探索图与树的算法

一、引言 在图论和树形结构中&#xff0c;搜索算法是寻找从起点到终点的路径的关键。其中&#xff0c;深度优先搜索&#xff08;DFS&#xff09;和广度优先搜索&#xff08;BFS&#xff09;是最常用且最基础的两种搜索算法。本文将详细介绍广度优先搜索&#xff08;BFS&#xf…

C#上位机与三菱PLC的通信03--MC协议之A-1E报文解析

1、MC协议帧 MC协议可以在串口通信&#xff0c;也可以在以太网通信&#xff0c;有A-1E和Qna-3E两种模式&#xff0c;这两种都是三菱PLC通信协议中比较常用的两种&#xff0c;一般我们使用比较多的是以太网通信&#xff0c;对于FX5U系列/Q系列/Qna系列/L系列的PLC&#xff0c;…

目标检测 | 卷积神经网络(CNN)详细介绍及其原理详解

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种深度学习模型&#xff0c;主要用于图像识别和计算机视觉任务。它的设计灵感来自于生物学中视觉皮层的工作原理。CNN的核心思想是通…

Stable Diffusion教程——使用TensorRT GPU加速提升Stable Diffusion出图速度

概述 Diffusion 模型在生成图像时最大的瓶颈是速度过慢的问题。为了解决这个问题&#xff0c;Stable Diffusion 采用了多种方式来加速图像生成&#xff0c;使得实时图像生成成为可能。最核心的加速是Stable Diffusion 使用了编码器将图像从原始的 3512512 大小转换为更小的 46…

Leetcode刷题笔记题解(C++):面试题 08.07. 无重复字符串的排列组合

思路&#xff1a;因为字符之间互不相同&#xff0c;故使用全排列的方式去解题&#xff1b; 字符串长度为n&#xff0c;将第一个字母分别与后面每一个字母进行交换&#xff0c;生成n种不同的全排列&#xff1b;再用第二个元素与后面每一个元素进行交换&#xff0c;生成n - 1种不…