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

本文主要是介绍一起玩儿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值,瞬间就到达边界值了。如果说,你就想把长按当作连续按下,那也要加入一定的延迟,控制一下重复的频率,避免这个按键程序瞬间就被执行很多次,应该是在一种受控的状态下重复才行。

仿真结果如下图所示:

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

这篇关于一起玩儿Proteus仿真(C51)——04. 直流电机的启停、加减速和正反转仿真(L298)(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/694481

相关文章

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(五):Blender锥桶建模

前言 本系列教程旨在使用UE5配置一个具备激光雷达+深度摄像机的仿真小车,并使用通过跨平台的方式进行ROS2和UE5仿真的通讯,达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础,Nav2相关的学习教程可以参考本人的其他博客Nav2代价地图实现和原理–Nav2源码解读之CostMap2D(上)-CSDN博客往期教程: 第一期:基于UE5和ROS2的激光雷达+深度RG

取得 Git 仓库 —— Git 学习笔记 04

取得 Git 仓库 —— Git 学习笔记 04 我认为, Git 的学习分为两大块:一是工作区、索引、本地版本库之间的交互;二是本地版本库和远程版本库之间的交互。第一块是基础,第二块是难点。 下面,我们就围绕着第一部分内容来学习,先不考虑远程仓库,只考虑本地仓库。 怎样取得项目的 Git 仓库? 有两种取得 Git 项目仓库的方法。第一种是在本地创建一个新的仓库,第二种是把其他地方的某个

控制反转 的种类

之前对控制反转的定义和解释都不是很清晰。最近翻书发现在《Pro Spring 5》(免费电子版在文章最后)有一段非常不错的解释。记录一下,有道翻译贴出来方便查看。如有请直接跳过中文,看后面的原文。 控制反转的类型 控制反转的类型您可能想知道为什么有两种类型的IoC,以及为什么这些类型被进一步划分为不同的实现。这个问题似乎没有明确的答案;当然,不同的类型提供了一定程度的灵活性,但

perl的学习记录——仿真regression

1 记录的背景 之前只知道有这个强大语言的存在,但一直侥幸自己应该不会用到它,所以一直没有开始学习。然而人生这么长,怎就确定自己不会用到呢? 这次要搭建一个可以自动跑完所有case并且打印每个case的pass信息到指定的文件中。从而减轻手动跑仿真,手动查看log信息的重复无效低质量的操作。下面简单记录下自己的思路并贴出自己的代码,方便自己以后使用和修正。 2 思路整理 作为一个IC d

浙大数据结构:04-树7 二叉搜索树的操作集

这道题答案都在PPT上,所以先学会再写的话并不难。 1、BinTree Insert( BinTree BST, ElementType X ) 递归实现,小就进左子树,大就进右子树。 为空就新建结点插入。 BinTree Insert( BinTree BST, ElementType X ){if(!BST){BST=(BinTree)malloc(sizeof(struct TNo

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑燃料电池和电解槽虚拟惯量支撑的电力系统优化调度方法》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源程序擅长文章解读,论文与完整源程序,等方面的知识,电网论文源程序关注python

跟我一起玩《linux内核设计的艺术》第1章(四)——from setup.s to head.s,这回一定让main滚出来!(已解封)

看到书上1.3的大标题,以为马上就要见着main了,其实啊,还早着呢,光看setup.s和head.s的代码量就知道,跟bootsect.s没有可比性,真多……这确实需要包括我在内的大家多一些耐心,相信见着main后,大家的信心和干劲会上一个台阶,加油! 既然上篇已经玩转gdb,接下来的讲解肯定是边调试边分析书上的内容,纯理论讲解其实我并不在行。 setup.s: 目标:争取把setup.

读软件设计的要素04概念的关系

1. 概念的关系 1.1. 概念是独立的,彼此间无须相互依赖 1.1.1. 一个概念是应该独立地被理解、设计和实现的 1.1.2. 独立性是概念的简单性和可重用性的关键 1.2. 软件存在依赖性 1.2.1. 不是说一个概念需要依赖另一个概念才能正确运行 1.2.2. 只有当一个概念存在时,包含另一个概念才有意义 1.3. 概念依赖关系图简要概括了软件的概念和概念存在的理

Matlab simulink建模与仿真 第十章(模型扩展功能库)

参考视频:simulink1.1simulink简介_哔哩哔哩_bilibili 一、模型扩展功能库中的模块概览         注:下面不会对Block Support Table模块进行介绍。 二、基于触发的和基于时间的线性化模块 1、Trigger-Based Linearization基于触发的线性化模块 (1)每次当模块受到触发时,都会调用linmod或者dlinmod函数

[苍穹外卖]-04菜品管理接口开发

效果预览 新增菜品 需求分析 查看产品原型分析需求, 包括用到哪些接口, 业务的限制规则 业务规则 菜品名称必须是唯一的菜品必须属于某个分类下, 不能单独存在新增菜品时可以根据情况选择菜品的口味每个菜品必须对应一张图片 接口设计 根据类型查询分类接口 文件上传接口 新增菜品接口 数据表设计 设计dish菜品表 和 dish_fl