当前位置: 首页 > news >正文

【51单片机学习】直流电机驱动(PWM)、AD/DA、红外遥控(外部中断)

一、直流电机驱动(PWM)

1.直流电机介绍

步进电机的旋转速度完全由编码的通电时间决定的,可以用于精密控制。
舵机内部是一个直流电机加一个控制器,引出三根线,分别是正负极和编码线,根据输出电平的时间来控制旋转臂固定在某个位置,常用于小车转向等。
无刷电机和空心杯电机的转速较快,适用于飞行器,无刷电机的功率较大。

下图中均为直流电机。

2.电机驱动电路

电机是一种功率比较大的负载,如果直接接在单片机的I/O口上是无法驱动的,而且可能会损坏单片机的I/O口,所以需要在电机和I/O口之间加驱动电路。

常见的驱动电路有以下两种驱动方式:

  1. 大功率器件直接驱动
    特点是驱动的电机只能朝一个方向转动,因为这种电路不具备调换电机正反方向的功能。
    基本结构相当于三极管开关,三极管需要选择一个功率比较大的器件,常见的是达林顿管或MOS管,基本原理与三极管开关一样,MOS管也是一种相当于电子开关的元件。
    IN输入低电平时,三极管导通,图中的二极管为续流二极管,因为电机是一种感性负载元件,驱动时需要注意它的感性值,电感会感应出很高的电压,当电路断开的瞬间,电感为保证电流不突变,会有电流通过续流二极管构成循环释放掉。
  2. H桥驱动
    这种驱动可以控制电机正反转。
    这里的电机没有办法加续流二极管,所以这就要求驱动的晶体管或MOS管具有很强的耐压特性,可以承受住感应电压。

在ULN2003中,输入1输出为0,输入0输出为1。

【补充】在电路中,如果没有任何感性元件,那么电路中任意两点之间的电压不可能高于电源电压,不具有升压特性,但是如果电路中有电感或其他感性元件,那么电路中产生的电压可能会高于电源电压,因为电感中的电流不能突变,所以会感应出高电压。

3.PWM介绍

单片机产生模拟变量的效果不是很好,但是很容易产生数字变量。

频率控制高低电平变化的速度,如果频率很高则最终的效果会比较好,如果频率比较低,那么电机可能会出现抖动。
精度越高,占空比调节越细致。

4.产生PWM方法

计数器相当于定时器的TH0、TL0计数器一样,会定时自增。
比较值在硬件PWM里是一个寄存器,可以写入一个固定值,这个比较值决定了占空比,在程序中可以定义一个变量,把它当做一个比较值。如果占空比不变,比较值通常是固定的。
最终通过比较大小实现I/O口的翻转,硬件上是硬件比较器,软件上是 if判断,将计数器的值和比较值进行比较,如果计数器的值小于比较值,就会输出0,反之,则输出1。(具体是小于输出0还是大于输出0,这涉及到PWM的输出极性,在程序中可调。这里以计数器的值小于比较值输出0为例。)

5.代码示例

(1)LED呼吸灯

该代码中将PWM写在了主循环中,此时主循环是不能做其他事情的,所以通常会将PWM写在定时器中。而且对于现在较高级的单片机来说,都会有硬件的PWM,因为不断翻转I/O口是一种比较简单且比较占用CPU的操作,所以通常会用硬件来实现,硬件实现通常会寄生到定时器中,也就是说定时器既可以定时还可以兼具PWM的任务。
但是该单片机中不具备该功能,于是采用定时器中断来实现。

main.c文件
#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;					//LED亮Delay(Time);			//延时TimeLED=1;					//LED灭Delay(100-Time);		//延时100-Time}}for(Time=100;Time>0;Time--)		//改变亮灭时间,由亮到暗{for(i=0;i<20;i++)			//计次延时{LED=0;					//LED亮Delay(Time);			//延时TimeLED=1;					//LED灭Delay(100-Time);		//延时100-Time}}}
}

(2)直流电机调速

需要提前加入Delay.c文件Delay.h文件Key.c文件Key.h文件Nixie.c文件Nixie.h文件Timer0.c文件Timer0.h文件

首先进行定时器初始化,但是定时器1ms进行一次中断时间太长,所以改为100us。
PWM驱动电机,在一定范围内是越快越好,越快越稳定,但是不能过快,会占用资源,会增加开关损耗,所以驱动电机时,通常将频率设置在10KHz~20KHz,频率较低时电机会出现抖动。
因为51单片机的主频较低,设置不了那么高的频率,所以这里设置为100us,则周期为10ms,这时电机会有少许抖动,但是不影响驱动。

计数器的值会影响精度和频率。

Counter%=100;

等价于

if(Counter>=100)
{

        Counter=0;

}

用定时器的方式实现PWM,可以把主循环留出来干其他事情,可以扫描按键、显示数码管等。

main.c文件
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Timer0.h"sbit Motor=P1^0;unsigned char Counter,Compare;	//计数值和比较值,用于输出PWM
unsigned char KeyNum,Speed;void main()
{Timer0_Init();while(1){KeyNum=Key();if(KeyNum==1){Speed++;Speed%=4;if(Speed==0){Compare=0;}	//设置比较值,改变PWM占空比if(Speed==1){Compare=50;}if(Speed==2){Compare=75;}if(Speed==3){Compare=100;}}Nixie(1,Speed);}
}void Timer0_Routine() interrupt 1
{TL0 = 0x9C;		//设置定时初值TH0 = 0xFF;		//设置定时初值Counter++;Counter%=100;	//计数值变化范围限制在0~99if(Counter<Compare)	//计数值小于比较值{Motor=1;		//输出1}else				//计数值大于比较值{Motor=0;		//输出0}
}

二、AD/DA

1.AD/DA介绍

下图所示依次为光敏电阻、热敏电阻、麦克风、扬声器。

2.硬件电路模型

AD一般是对电压进行转化的,只能读取电压。

AD经常多路复用,DA一般只有一个通道,因为AD转化可以是离散的,但是模拟信号是连续的,所以不方便切换通道,一切换通道信号就断开了,一般只用一个通道进行输出。AD较常用,DA使用的比较少,经常用PWM代替。

3.硬件电路

ADC芯片实际是触摸屏的芯片,触摸屏的原理就是AD转换的原理。

PCF891是I2C总线的,通过I2C总线把数据写入和读出,就可以实现AD/DA的功能,操作简单。

ADC0809,将电压转化为8位数据输出,输入也有8个通道,经过模拟开关选择一路进行A/D转换,地址锁存与译码控制模拟开关选择哪一路。

DAC0832,输入8位数据,有寄存器缓存,由控制信号进行控制,缓存器的作用是用来多路同步,例如要求两个DAC同时输出,可以先将数据存放在缓存器中,再同时给输出信号,实现两个DAC的同步输出。

4.运算放大器

运算放大器的特性:输入阻抗非常大,输出端具有驱动能力,开环增益无穷大。可以通过深度负反馈调节放大倍数。

5.运放电路

在使用反向放大器时,若想输出负电压,应使用双电源供电。

电压跟随器可以提高信号的驱动能力。当运放的放大倍数是1时,输入没有电压放大的特性,但是有功率放大的特性。

6.DA原理

T型电阻网络DA转换器,相当于上面所说的DAC0832的8位D/A转换器。

下面是8位输入数字量D0~D7,通过高低电平控制模拟开关,最终的输出是运放的输出。精度不可改变,始终是256。

通过低通滤波器滤除交流分量,剩下的直流分量就是D/A的输出。因为分压结构的驱动能力弱,如果接负载会影响滤波性能,为了隔离后面的输出电路,一般会加入电压跟随器,增强驱动能力。
二阶低通滤波电路的滤波效果更好,如果滤除交流分量的效果不太好的话,直流分量会产生纹波。

PWM型DA转换器的好处是节省I/O口,只需要一个I/O口输出PWM波即可,且精度比较高,直接调节PWM占空比的精细程度即可。坏处是比较消耗单片机的资源需要输出PWM波形,且低通滤波器性能不好的话输出电压会有一定的纹波。

7.AD原理

未知信号与DAC输出的电压进行比较,如果未知信号大,就把DAC升高,如果未知信号小,就把DAC降低,最终让这两个电压接近相等,此时便可以用已知表示未知。

8.AD/DA性能指标

AD通道个数,一般的AD是8个通道的,如果此阿阳很多的话,就需要一个通道个数比较多的AD。

9.XPT2046

10.XPT2046时序

XPT2046是SPI通信,也可以复用,即一条总线上挂多个负载。三根通信线:时钟线和输入、输出线是共用的。每个芯片单独有一个CS片选,在同一时间只选中一片。
输入、输出线有时也写为MISO(主设备输入从设备输出)、MOSI(主设备输出从设备输入)。
DIN和DOUT是针对于从设备来说的。

片选→上升沿输入,下降沿输出,有时DIN和DOUT可以同时进行。
发送一个字节,连续读取两个字节16位,多余位用0填充。

11.代码示例

(1)AD模数转换

需要提前加入Delay.c文件Delay.h文件LCD1602.c文件LCD1602.h文件。

main.c文件
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "XPT2046.h"unsigned int ADValue;void main(void)
{LCD_Init();LCD_ShowString(1,1,"ADJ  NTC  GR");while(1){ADValue=XPT2046_ReadAD(XPT2046_XP);		//读取AIN0,可调电阻LCD_ShowNum(2,1,ADValue,3);				//显示AIN0ADValue=XPT2046_ReadAD(XPT2046_YP);		//读取AIN1,热敏电阻LCD_ShowNum(2,6,ADValue,3);				//显示AIN1ADValue=XPT2046_ReadAD(XPT2046_VBAT);	//读取AIN2,光敏电阻LCD_ShowNum(2,11,ADValue,3);			//显示AIN2Delay(100);}
}
XPT2046.c文件
 #include <REGX52.H>
#include <INTRINS.H>//引脚定义
sbit XPY2046_DIN=P3^4;
sbit XPY2046_CS=P3^5;
sbit XPY2046_DCLK=P3^6;
sbit XPY2046_DOUT=P3^7;/*** @brief  ZPT2046读取AD值* @param  Command 命令字,范围:头文件内定义的宏,结尾的数字表示转换的位数* @retval AD转换后的数字量,范围:8位为0~255,12位为0~4095*/
unsigned int XPT2046_ReadAD(unsigned char Command)
{unsigned char i;unsigned int Data=0;XPY2046_DCLK=0;		//初始化XPY2046_CS=0;for(i=0;i<8;i++){XPY2046_DIN=Command&(0x80>>i);XPY2046_DCLK=1;XPY2046_DCLK=0;}for(i=0;i<16;i++)	//读取两个字节{XPY2046_DCLK=1;XPY2046_DCLK=0;if(XPY2046_DOUT){Data|=(0x8000>>i);}}XPY2046_CS=1;return Data>>8;
}
XPT2046.h文件
#ifndef __XPT2046_H__
#define __XPT2046_H__#define XPT2046_VBAT	0xAC
#define XPT2046_AUX		0xEC
#define XPT2046_XP		0x9C	//0xBC
#define XPT2046_YP		0xDCunsigned int XPT2046_ReadAD(unsigned char Command);#endif

(2)DA数模转换

需要提前加入Delay.c文件Delay.h文件Timer0.c文件Timer0.h文件。

PWM型的DA本身就是PWM,只不过在硬件上增加了一个低通滤波。

main.c文件
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"sbit DA=P2^1;unsigned char Counter,Compare;	//计数值和比较值,用于输出PWM
unsigned char i;void main()
{Timer0_Init();while(1){for(i=0;i<100;i++){Compare=i;			//设置比较值,改变PWM占空比Delay(10);}for(i=100;i>0;i--){Compare=i;			//设置比较值,改变PWM占空比Delay(10);}}
}void Timer0_Routine() interrupt 1
{TL0 = 0x9C;		//设置定时初值TH0 = 0xFF;		//设置定时初值Counter++;Counter%=100;	//计数值变化范围限制在0~99if(Counter<Compare)	//计数值小于比较值{DA=1;		//输出1}else				//计数值大于比较值{DA=0;		//输出0}
}

三、红外遥控(外部中断)

1.红外遥控简介

调制的目的是增强抗干扰性。

这里的红外LED的波长是940nm,人眼不可见。有一种红外LED的波长是850nm,这种LED人眼可以看到微弱的红光,利用红外进行补光,例如监控摄像头在夜晚时周围亮的红光。

2.硬件电路

开发板上不具有红外发送部分的电路,所以只能使用配备的遥控器进行发送。

将红外接收头输出的波形进行解码,读取键码值即可实现功能。

红外发送部分,是由两个串联的三极管开关、红外LED和限流电阻构成的。38KHz的调制频率会一直输入一个同频率的方波,将想要输入的波形输入到IN口,当IN口是高电平时,LED不良,当IN口是低电平时,LED以38KHz的频率闪烁,这么做的目的是抗干扰,因为自然界中也有很多红外光,例如太阳的红外光,如果LED发出连续的红外光,因为太阳的红外光明显更强,会将输出的红外光淹没在太阳光中,导致红外接收头无法判断。
在使用第二种发送部分是,需要软件内部实现“闪着亮”,即I/O口直接输出低电平不亮,高电平时以38KHz闪着亮的波形,这个调制就是把高低电平和38KHz进行叠加。

接收部分有单独的红外接收LED,如果直接接收的话,还会接收到其他干扰光,所以需要在后面的电路设计一些电路把多余的干扰光滤除掉,使最终输出的信号与发送的信号相同。也可以购买一体化红外接收头,这里面包含了红外接收管和一些处理电路。

因为信号发生的很快,每次按键按下之后都会出现很多的高低电平,这些高低电平会在几十毫秒内跑完,所以不能用单片机判断按键那样使用 if语句 进行循环扫描,这样速度太慢了。他的波形在每个下降沿之间的时间间隔很短,为了更快地处理,需要把OUT引脚连接在外部中断的引脚上(P3.2和P3.3),如果产生了一个下降沿就立刻产生中断对他进行计时处理,这样的话响应的实时性会提高。

3.基本发送与接收

上电默认高电平。

4.NEC编码

38KHz的载波频率只针对于底层通信,NEC编码中并不会出现38KHz的调制。相当于底层做好了基本的发送高低电平,然后封装在模块中,如果想发送高低电平,先调制再接收最后解调,标准是建立在输入信号和输出信号之上的。

下图中的波形是遥控器的按键按下之后,接收头OUT引脚输出的波形。

Data格式总共四个字节,每个字节8位,四个字节一共32位,第一个字节表示地址码,是遥控器的一个标识符,防止不同品牌的遥控器互相使用;地址码反码是地址码按位取反,目的是为了进行数据验证,接收完32bit的数据之后,把第二个字节取反看看是否与第一个字节相等;命令码代表键码;命令反码也是进行验证的。所以四个字节实际上只有两个字节携带信息。每个字节都是低位在前,高位在后

低电平:560us低电平+560us高电平;高电平:560us低电平+1690us高电平。

Repeat是重发,如果按键一直保持按下的状态,每隔100ms就会发送一次Repeat,相当于连续案件的功能。

示波器实际采样结果如下图。

以KEY2为例,最后多出的一个下降沿是为了终止最后一位。

5.遥控器键码

6.51单片机的外部中断

传统的89C51只有两个外部中断。这里在解码的时候用的是下降沿触发,测量两个下降沿之间的时间,就可以知道信号的类型。

7.外部中断寄存器

外部中断的结构比定时器、串口等更加简单。红框选中的是外部中断。

8.代码示例

(1)红外遥控

需要提前加入Delay.c文件Delay.h文件LCD1602.c文件LCD1602.h文件。

低电平触发,只要是低电平中断会一直处于触发状态,中断函数结束之后会再次进入,直到变成高电平为止。现象表现为长按不松手数值会一直增加,直到松手数值才会停止增加。

不推荐将外部中断当做按键检测来使用,因为按键是有抖动的,可能按一次触发多次外部中断,而且外部中断只能下降沿触发,不能做成松手触发。外部中断的引脚只有两个,比较少,一般不用外部中断来做按键检测。按键检测一般使用主循环或者定时器检测。

&=:两个二进制的对应位都为1时,结果为1,否则结果等于0;
|=:两个二进制对应位都为0时,结果等于0,否则结果等于1;
^=:两个二进制的对应位相同,结果为0,否则结果为1。

main.c文件
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"unsigned char Num;
unsigned char Address;
unsigned char Command;void main()
{LCD_Init();LCD_ShowString(1,1,"ADDR  CMD  NUM");LCD_ShowString(2,1,"00    00   000");IR_Init();while(1){if(IR_GetDataFlag() || IR_GetRepeatFlag())	//如果收到数据帧或者收到连发帧{Address=IR_GetAddress();		//获取遥控器地址码Command=IR_GetCommand();		//获取遥控器命令码LCD_ShowHexNum(2,1,Address,2);	//显示遥控器地址码LCD_ShowHexNum(2,7,Command,2);	//显示遥控器命令码if(Command==IR_VOL_MINUS)		//如果遥控器VOL-按键按下{Num--;						//Num自减}if(Command==IR_VOL_ADD)			//如果遥控器VOL+按键按下{Num++;						//Num自增}LCD_ShowNum(2,12,Num,3);		//显示Num}}
}
Int0.c文件
#include <REGX52.H>/*** @brief  外部中断0初始化* @param  无* @retval 无*/
void Int0_Init(void)
{IT0=1;IE0=0;EX0=1;EA=1;PX0=1;
}/*外部中断0中断函数模板
void Int0_Routine(void) interrupt 0
{}
*/
Int0.h文件
#ifndef __INT0_H__
#define __INT0_H__void Int0_Init(void);#endif
Timer0.c文件
#include <REGX52.H>/*** @brief  定时器0初始化* @param  无* @retval 无*/
void Timer0_Init(void)
{TMOD &= 0xF0;		//设置定时器模式TMOD |= 0x01;		//设置定时器模式TL0 = 0;		//设置定时初值TH0 = 0;		//设置定时初值TF0 = 0;		//清除TF0标志TR0 = 0;		//定时器0不计时
}/*** @brief  定时器0设置计数器值* @param  Value,要设置的计数器值,范围:0~65535* @retval 无*/
void Timer0_SetCounter(unsigned int Value)
{TH0=Value/256;TL0=Value%256;
}/*** @brief  定时器0获取计数器值* @param  无* @retval 计数器值,范围:0~65535*/
unsigned int Timer0_GetCounter(void)
{return (TH0<<8)|TL0;
}/*** @brief  定时器0启动停止控制* @param  Flag 启动停止标志,1为启动,0为停止* @retval 无*/
void Timer0_Run(unsigned char Flag)
{TR0=Flag;
}
Timer0.h文件
#ifndef __TIMER0_H__
#define __TIMER0_H__void Timer0_Init(void);
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter(void);
void Timer0_Run(unsigned char Flag);#endif
IR.c文件
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"unsigned int IR_Time;
unsigned char IR_State;unsigned char IR_Data[4];
unsigned char IR_pData;unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;/*** @brief  红外遥控初始化* @param  无* @retval 无*/
void IR_Init(void)
{Timer0_Init();Int0_Init();
}/*** @brief  红外遥控获取收到数据帧标志位* @param  无* @retval 是否收到数据帧,1为收到,0为未收到*/
unsigned char IR_GetDataFlag(void)
{if(IR_DataFlag){IR_DataFlag=0;return 1;}return 0;
}/*** @brief  红外遥控获取收到连发帧标志位* @param  无* @retval 是否收到连发帧,1为收到,0为未收到*/
unsigned char IR_GetRepeatFlag(void)
{if(IR_RepeatFlag){IR_RepeatFlag=0;return 1;}return 0;
}/*** @brief  红外遥控获取收到的地址数据* @param  无* @retval 收到的地址数据*/
unsigned char IR_GetAddress(void)
{return IR_Address;
}/*** @brief  红外遥控获取收到的命令数据* @param  无* @retval 收到的命令数据*/
unsigned char IR_GetCommand(void)
{return IR_Command;
}//外部中断0中断函数,下降沿触发执行
void Int0_Routine(void) interrupt 0
{if(IR_State==0)				//状态0,空闲状态{Timer0_SetCounter(0);	//定时计数器清0Timer0_Run(1);			//定时器启动IR_State=1;				//置状态为1}else if(IR_State==1)		//状态1,等待Start信号或Repeat信号{IR_Time=Timer0_GetCounter();	//获取上一次中断到此次中断的时间Timer0_SetCounter(0);	//定时计数器清0//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)if(IR_Time>13500-500 && IR_Time<13500+500){IR_State=2;			//置状态为2}//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)else if(IR_Time>11250-500 && IR_Time<11250+500){IR_RepeatFlag=1;	//置收到连发帧标志位为1Timer0_Run(0);		//定时器停止IR_State=0;			//置状态为0}else					//接收出错{IR_State=1;			//置状态为1}}else if(IR_State==2)		//状态2,接收数据{IR_Time=Timer0_GetCounter();	//获取上一次中断到此次中断的时间Timer0_SetCounter(0);	//定时计数器清0//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)if(IR_Time>1120-500 && IR_Time<1120+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));	//数据对应位清0IR_pData++;			//数据位置指针自增}//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)else if(IR_Time>2250-500 && IR_Time<2250+500){IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));	//数据对应位置1IR_pData++;			//数据位置指针自增}else					//接收出错{IR_pData=0;			//数据位置指针清0IR_State=1;			//置状态为1}if(IR_pData>=32)		//如果接收到了32位数据{IR_pData=0;			//数据位置指针清0if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3]))	//数据验证{IR_Address=IR_Data[0];	//转存数据IR_Command=IR_Data[2];IR_DataFlag=1;	//置收到连发帧标志位为1}Timer0_Run(0);		//定时器停止IR_State=0;			//置状态为0}}
}
IR.h文件
#ifndef __IR_H__
#define __IR_H__#define IR_POWER		0x45
#define IR_MODE			0x46
#define IR_MUTE			0x47
#define IR_START_STOP	0x44
#define IR_PREVIOUS		0x40
#define IR_NEXT			0x43
#define IR_EQ			0x07
#define IR_VOL_MINUS	0x15
#define IR_VOL_ADD		0x09
#define IR_0			0x16
#define IR_RPT			0x19
#define IR_USD			0x0D
#define IR_1			0x0C
#define IR_2			0x18
#define IR_3			0x5E
#define IR_4			0x08
#define IR_5			0x1C
#define IR_6			0x5A
#define IR_7			0x42
#define IR_8			0x52
#define IR_9			0x4Avoid IR_Init(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAddress(void);
unsigned char IR_GetCommand(void);#endif

(2)红外遥控电机调速

需要提前加入Delay.c文件Delay.h文件Key.c文件Key.h文件Nixie.c文件Nixie.h文件Int0.c文件Int0.h文件IR.c文件IR.h文件Timer0.c文件Timer0.h文件

main.c文件
#include <REGX52.H>
#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Motor.h"
#include "IR.h"unsigned char Command,Speed;void main()
{Motor_Init();IR_Init();while(1){if(IR_GetDataFlag())	//如果收到数据帧{Command=IR_GetCommand();		//获取遥控器命令码if(Command==IR_0){Speed=0;}		//根据遥控器命令码设置速度if(Command==IR_1){Speed=1;}if(Command==IR_2){Speed=2;}if(Command==IR_3){Speed=3;}if(Speed==0){Motor_SetSpeed(0);}	//速度输出if(Speed==1){Motor_SetSpeed(50);}if(Speed==2){Motor_SetSpeed(75);}if(Speed==3){Motor_SetSpeed(100);}}Nixie(1,Speed);						//数码管显示速度}
}
Timer1.c文件
#include <REGX52.H>/*** @brief  定时器1初始化,100us@12.000MHz* @param  无* @retval 无*/
void Timer1_Init(void)
{TMOD &= 0x0F;		//设置定时器模式TMOD |= 0x10;		//设置定时器模式TL1 = 0x9C;		//设置定时初值TH1 = 0xFF;		//设置定时初值TF1 = 0;		//清除TF1标志TR1 = 1;		//定时器1开始计时ET1=1;EA=1;PT1=0;
}/*定时器中断函数模板
void Timer1_Routine() interrupt 3
{static unsigned int T1Count;TL1 = 0x9C;		//设置定时初值TH1 = 0xFF;		//设置定时初值T1Count++;if(T1Count>=1000){T1Count=0;}
}
*/
Timer1.h文件
#ifndef __TIMER1_H__
#define __TIMER1_H__void Timer1_Init(void);#endif
Motor.c文件
#include <REGX52.H>
#include "Timer1.h"//引脚定义
sbit Motor=P1^0;unsigned char Counter,Compare;/*** @brief  电机初始化* @param  无* @retval 无*/
void Motor_Init(void)
{Timer1_Init();
}/*** @brief  电机设置速度* @param  Speed 要设置的速度,范围0~100* @retval 无*/
void Motor_SetSpeed(unsigned char Speed)
{Compare=Speed;
}//定时器1中断函数
void Timer1_Routine() interrupt 3
{TL1 = 0x9C;		//设置定时初值TH1 = 0xFF;		//设置定时初值Counter++;Counter%=100;	//计数值变化范围限制在0~99if(Counter<Compare)	//计数值小于比较值{Motor=1;		//输出1}else				//计数值大于比较值{Motor=0;		//输出0}
}
Motor.h文件
#ifndef __MOTOR_H__
#define __MOTOR_H__void Motor_Init(void);
void Motor_SetSpeed(unsigned char Speed);#endif
http://www.xdnf.cn/news/1352521.html

相关文章:

  • mmdetection:记录算法训练配置文件
  • A Large Scale Synthetic Graph Dataset Generation Framework的学习笔记
  • Mysql EXPLAIN详解:从底层原理到性能优化实战
  • 如何在Ubuntu中删除或修改已有的IP地址设置?
  • C语言---数据类型
  • PyTorch生成式人工智能——VQ-VAE详解与实现
  • TypeScript 的泛型(Generics)作用理解
  • Kafka 概念与概述
  • 在TencentOS3上部署OpenTenBase:从入门到实战的完整指南
  • 【Java学习笔记】18.反射与注解的应用
  • 遥感机器学习入门实战教程|Sklearn案例⑧:评估指标(metrics)全解析
  • tcpdump命令打印抓包信息
  • 【golang】ORM框架操作数据库
  • 2-5.Python 编码基础 - 键盘输入
  • STM32CubeIDE V1.9.0下载资源链接
  • 醋酸镨:催化剂领域的璀璨新星
  • LangChain4J-基础(整合Spring、RAG、MCP、向量数据库、提示词、流式输出)
  • 信贷模型域——信贷获客模型(获客模型)
  • 温度对直线导轨的性能有哪些影响?
  • 小白向:Obsidian(Markdown语法学习)快速入门完全指南:从零开始构建你的第二大脑(免费好用的笔记软件的知识管理系统)、黑曜石笔记
  • 数字经济、全球化与5G催生域名新价值的逻辑与实践路径
  • 快速掌握Java非线性数据结构:树(二叉树、平衡二叉树、多路平衡树)、堆、图【算法必备】
  • vue3 - 组件间的传值
  • 【小沐学GIS】基于Godot绘制三维数字地球Earth(Godot)
  • 计算机网络 TLS握手中三个随机数详解
  • 【Golang】有关垃圾收集器的笔记
  • 语义通信高斯信道仿真代码
  • GaussDB 数据库架构师修炼(十八) SQL引擎-计划管理-SQL PATCH
  • Base64编码、AES加密、RSA加密、MD5加密
  • RAG Embeddings 向量数据库