本实验基于51单片机控制小风扇,在软件keil5上编写程序。需要使用到的元件有51单片机、直流电机、舵机、红外遥控器、杜邦线、蜂鸣器、数码管。需要完成NEC红外遥控器解码、舵机转动控制、电机转速控制、蜂鸣器发声四部分程序的编写,最后将这四部分合并再将相应元件组装,完成一个基于51单片机的红外摇头小风扇的设计。同时能够在数码管显示直流电机转速的档位,一个有0~10这一个档位,0就是停止,10最大。
舵机是由直流电机、减速齿轮组、传感器和控制电路组成的一套自动控制系统。通过发送信号,指定输出轴旋转角度。舵机的控制一般需要一个20ms左右的时基脉冲,该脉冲的高电平部分一般为0.5ms-2.5ms范围内的角度控制脉冲部分,总间隔为2ms。以180度角度伺服为例,那么对应的控制关系是这样的:0.5ms--------------0度; 1.0ms------------45度;1.5ms------------90度;2.0ms-----------135度; 2.5ms-----------180度。舵机上有三根线,红黄褐三种颜色,褐色线为舵机信号线,通过给信息线提供不同的电信号来控制舵机转过不同的角度。
舵机的PWM信号频率改变一定要有延时,要给舵机足够的时间转到指定的角度后才能改变PWM频率。
使用51单片机的定时器2控制中断
#include
int flag=0;
sbit pwm1=P1^1;
sbit key1=P3^2;//开关1引脚
unsigned int value[]={6,11,16,21,16,11};//误差原因,6-0°,11-45°,16-90°,21-135°,26-180°
unsigned int percent = 0;//0.1ms次数标识
int k=0,count=0;
void Timer0Initial()
{
T2L = 0xAE;
T2H = 0xFB;
} //定时0.1ms,11.0592MHz,0.01%的误差
void initial_Timer()
{
AUXR |= 0x04; //设置1T模式
Timer0Initial();
AUXR |= 0x10;//定时器2开始计时
IE2 |=0x04; //允许定时器2中断
EA = 1; //打开总中断
}
void delay(unsigned int x)
{
unsigned int i,j;
for(i = 0;i< x; ++i)
for(j = 120; j >0 ; --j)
;
}//延时函数,1ms
void keyscan()//按键扫描函数
{
if(key1 == 0)
{
delay(3);//按键消抖,延时消抖
if(key1 == 0)
{
while(!key1);//按键放开
flag=!flag;
}
}
}
void initial()
{
key1 = 1;
initial_Timer();
}//初始化
void Timer0() interrupt 12 //定时器2
{
if(flag==1){//若打开开关
percent += 1;//设置周期
if(percent == 200)
{
count++;
percent = 0;
pwm1 = 1;
}//产生低电平,当达到20ms时,进入下个周期,重新产生蹈叩缙?
if(percent == value[k])
{
pwm1 = 0;
}//产生高电平,当时间达到时,变为低电平
if(count==20){
count=0;
k++;
if(k==6)k=0;
}//,13ms/60°,延时,给舵机时间转动到指定的角度
}
Timer0Initial();
}
void main()
{
initial();
while(1)
{
keyscan();
delay(10);
}
}
在PWM驱动控制的调整系统中,按一个固定的频率来接通和断开电源,并且根据需要改变一个周期内“接通”和“断开”时间的长短。通过改变直流电机电枢上电压的“占空比”来达到改变平均电压大小的目的,从而来控制电动机的转速。
如图1所示:
图1 PWM信号的占空比
设电机始终接通电源时,电机转速最大为Vmax,设占空比为D= t1 / T,则电机的平均速度为Va = Vmax * D,其中Va指的是电机的平均速度;Vmax 是指电机在全通电时的最大速度;D = t1 / T是指占空比。由上面的公式可见,当我们改变占空比D=t1/T时,就可以得到不同的电机平均速度Vd,从而达到调速的目的。严格来说,平均速度Vd与占空比D并非严格的线性关系,但是在一般的应用中,我们可以将其近似的看成是线性关系。所以,我们通过改变高电平持续的时间来进行直流电机调速。
使用51单片机的定时器0来控制中断
#include
sbit Key1=P3 ^ 2; //风扇减速
sbit Key2=P3 ^ 3; //风扇加速
sbit Key3=P1 ^ 7; //风扇停止或者开始
sbit pwm1=P4^1;
sbit pwm2=P4^2;
bit fflag=0;//风扇转动标志位
int speed = 50,count = 0;
/****************延时处理**********************/
void Delay( unsigned int xms )
{
unsigned char i;
for( ; xms > 0; xms-- )
for( i = 114; i > 0; i-- )
{
;
}
}
/****************初始化**********************/
void init(){
P4M0=0x06;
P4M1=0x00;//设置P4.1,P4.2为推挽输出
pwm1=0;
pwm2=0;
AUXR |= 0x80; //定时器时钟1T模式
TMOD = 0x01; //设置定时器模式,16位自动重装载
TL0 = (65536-1000)%256;
TH0 = (65536-1000)/256;
TF0=0;
TR0=1;//定时器0开始计时
ET0=1;//打开定时器0中断
EA=1;//打开总中断
}
/************按键处理函数***************/
void keys(){
if(Key3==0){
Delay(10);
if(Key3==0){
fflag=~fflag;
}
while(!Key3);
}
if(Key1==0){
Delay(10);
if(Key1==0){
speed-=5;
if(speed<=0) speed=0;
}
while(!Key1);
}
if(Key2==0){
Delay(10);
if(Key2==0){
speed+=5;
if(speed>=100)speed=100;
}
while(!Key2);
}
}
/***********中断处理函数**********/
void timer0()interrupt 1
{
TR0 = 0;
TL0 = (65536-1000)%256;
TH0 = (65536-1000)/256;
TR0 = 1;
if(fflag==1){
count++;
if(count>100){
count=0;
}
if(count<=speed){
pwm1=1;
}
else pwm1=0;
}
}
void main()
{
init();
while(1){
keys();
}
}
NEC 协议使用38 kHz 载波频率,其数据格式包括了引导码、用户码、用户码(或者用户码反码)、按键键码和键码反码,最后一个停止位。停止位主要起隔离作用,一般不进行判断。其中数据编码总共是 4 个字节 32 位,如图 2 所示。第一个字节是用户码,第二个字节可能也是用户码,或者是用户码的反码,具体由生产商决定,第三个字节就是当前按键的键数据码,而第四个字节是键数据码的反码,可用于对数据的纠错。
图 2 NEC 协议数据格式
• 引导码:9ms 的载波+4.5ms 的空闲。
• 比特值“0”:560us 的载波+560us 的空闲。
• 比特值“1”:560us 的载波+1.68ms 的空闲。
如上图所示,当一体化接收头收到38kHz红外信号时,载波为低电平,空闲为高电平
使用51单片机的使用定时器1控制中断
#include
#define uchar unsigned char
#define uint unsigned int
sbit led_sel=P2^3;
sbit IRIN=P3^6;
uchar irtime = 0;
uchar startflag = 0; // 起始标志位
uchar ir_rc_ok = 0; // 红外高/低电平时间接收完成标志
uchar bitnum = 0;
uchar ir_change_ok = 0; // 转码完成标志
uchar irdata[33]; // 电平时间数组
uchar ircode[4]={0,0,0,0}; // 遥控解码数组
/********* 延时函数 **********/
void delayms(uint xms)
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
/********** 系统初始化 ********/
void sysinit()
{
// 初始化定时器
TMOD |= 0x20; // 设置定时器1位工作方式2 (0~255)
TH1 = 0x00; // 赋初值
TL1 = 0x00; 256*(1/12m)*12=0.256ms
TR1 = 1; // 启动定时器1
ET1 = 1; // 使能定时器1
// 初始化外部中断2
INT_CLKO |= 0x10;//开INT2;
EA = 1; // 开总中断
}
/********* LED灯初始化 **********/
void IOinit(){
P0M0=0xff;
P0M1=0x00;
P2M1=0x00;
P2M0=0x08;
led_sel=1;
P0=0x00;
}
/************ 转换编码 *************/
void changecode()
{
uchar k = 1;
uchar i,j;
uchar value;
if(ir_rc_ok ==1) // 如果ir接受完成就进行转码
{
for(i=0;i<4;i++) // 获得前导码后的4个
{
for(j=0;j<8;j++)//8位
{
value = value>>1; //
if(irdata[k]>6&&irdata[k]<10) // 判断数据是否为" 1 "
{
value |= 0x80;
}
k++;
}
ircode[i] = value; // 存储
}
if(ircode[2]==~ircode[3]){
ir_change_ok = 1; // 编码转换完成标志
ir_rc_ok = 0; // ir接收标志复位
}
}
}
/********* 定时器1中断服务函数 ***********/
void t0()interrupt 3
{
irtime++;
}
/********* INT2处理函数 ***********/
void exint1() interrupt 10
{
if(startflag)
{
if(irtime>32&&irtime<63) // 检测前导码
{
bitnum=0;
}
irdata[bitnum]=irtime; // 从前导码开始接收数据
irtime=0;
bitnum++;
if(bitnum==33) // 前导码+4字节数据,共33比特
{
bitnum=0;
ir_rc_ok = 1;
}
}
else
{
startflag = 1;
irtime = 0;
}
}
void main()
{
IOinit();
sysinit(); // 初始化
while(1)
{
changecode(); // 解码
if(ir_change_ok == 1) {
P0=ircode[2];
ir_change_ok =0;
}
}
}
无源蜂鸣器只需改变Beep端口的电平,产生一个周期性的方波即可使蜂鸣器发生声音,不同的频率发出的声音不同。其中,ULN2003是一个功放,用于放大电流。电阻R14和电容C21是用来保护电路的。若人为将Beep端口的电平一直置为高电平,在没有保护电路的情况下,容易烧毁电路,但即使有保护电路也应该注意不要将Beep端口长时间置于高电平,这对器件也是有一定损害的。
#include
#define uint unsigned int
#define uchar unsigned char
/*---------引脚别名定义---------*/
sbit sbtBeep = P3 ^ 4; //蜂鸣器引脚
sbit sbtKey1 = P3 ^ 2; //按键1引脚
/*---------初始化函数--------*/
void init()
{
P3M1 = 0x00;
P3M0 = 0x10; //设置P3^4为推挽模式
P0 = 0x00; //关闭P0端口
sbtBeep = 0; //蜂鸣器引脚置0,以保护蜂鸣器
}
uint xb=0;
/*---------延时子函数--------*/
void delay( uint xms )
{
uchar i;
for( ; xms > 0; xms-- )
for( i = 114; i > 0; i-- )
{
;
}
}
/*---------蜂鸣器发声函数--------*/
void beeps(){
for(xb=0;xb<255;xb++){
sbtBeep=1;
delay(10);
sbtBeep=0;
delay(10);
}
}
/*---------主函数--------*/
void main()
{
init();
while( 1 )
{
if( sbtKey1 == 0 )
{
delay( 10 ); //延时消抖
if( sbtKey1 == 0 )
{
while( !sbtKey1 ); //松手后才响
beeps();
}
}
}
}
github代码链接
舵机和直流电机都是通过PWM信号控制,舵机利用PWM信号转动到指定的角度,而直流电机利用PWM信号进行调速。舵机的PWM信号频率改变一定要有延时,要给舵机足够的时间转到指定的角度后才能改变PWM频率。在程序运行时,各个定时器,各部分中断都是独立运行的,互不干扰,红外遥控解码就是利用中断INT2和定时器同时独立运行实现解码。蜂鸣器因为只要短暂发声,所以不需要使用定时器来定时,利用循环,循环次数增多,一次循环实现beep信号的一个周期性变化,这样就能完成短暂发声。在实验过程中,我发现当LED灯数码管同时亮时,LED灯的亮度要比只有LED灯亮时要暗,所以我要LED灯交替到数码管亮时的延迟增加,这样就能让LED灯数码管同时亮时LED灯更亮。原理是通过增加LED灯交替到数码管亮时的延迟,改变脉冲的高电平的时间,即LED的导通时间,这样改变了接入到LED灯周期信号的占空比,就改变了LED灯的亮度