一、直流电机驱动(PWM)
1.直流电机介绍
步进电机的旋转速度完全由编码的通电时间决定的,可以用于精密控制。
舵机内部是一个直流电机加一个控制器,引出三根线,分别是正负极和编码线,根据输出电平的时间来控制旋转臂固定在某个位置,常用于小车转向等。
无刷电机和空心杯电机的转速较快,适用于飞行器,无刷电机的功率较大。
下图中均为直流电机。
2.电机驱动电路
电机是一种功率比较大的负载,如果直接接在单片机的I/O口上是无法驱动的,而且可能会损坏单片机的I/O口,所以需要在电机和I/O口之间加驱动电路。
常见的驱动电路有以下两种驱动方式:
- 大功率器件直接驱动
特点是驱动的电机只能朝一个方向转动,因为这种电路不具备调换电机正反方向的功能。
基本结构相当于三极管开关,三极管需要选择一个功率比较大的器件,常见的是达林顿管或MOS管,基本原理与三极管开关一样,MOS管也是一种相当于电子开关的元件。
IN输入低电平时,三极管导通,图中的二极管为续流二极管,因为电机是一种感性负载元件,驱动时需要注意它的感性值,电感会感应出很高的电压,当电路断开的瞬间,电感为保证电流不突变,会有电流通过续流二极管构成循环释放掉。 - 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