【51单片机实例】智能小车(二)-------- 小车的红外遥控调速

news/2024/11/30 7:39:52/

51智能小车系列

智能小车(一)--------小车的前进、后退和停止
智能小车(二)-------- 小车的红外遥控调速
智能小车(三)-------- 小车的红外循迹


文章目录

  • 前言
  • 一、红外遥控
    • 1. 简介
    • 2. 红外遥控系统
      • 2.1 红外发射
      • 2.2 红外接收
    • 3. 红外调制
      • 基本发送与接收
  • 二、中断系统
    • 1. 外部中断0 与 定时器中断0 简介
    • 2. 相关寄存器介绍
  • 三、代码实现
    • 1.延迟模块
    • 2. 定时器模块
      • 2.1 定时器0
      • 2.2 定时器1
    • 3. 数码管模块
    • 4. 独立按键模块
    • 5. 外部中断0模块
    • 6. 红外遥控模块
    • 7. 主函数模块
  • 总结


前言

本节我主要介绍的是基于51单片机下智能小车的简单应用,是在上一节“智能小车(一)”的基础上增添一些功能,比如:通过调节电机转动的占空比,实现对小车简单的pwm调速;在调速的基础上,又增加红外遥控模块,实现对小车的红外遥控调速或增加红外循迹模块等,之后我都会带大家一步步走进51单片机的具体应用中来。

相关硬件模块介绍还请前往上一节内容 “ 智能小车(一) ”


一、红外遥控

1. 简介

红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点。 由于红外线为不可见光,因此对环境影响很小,再由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影 响临近的无线电设备。

红外线是波长在760nm~1mm之间的非可见光。红外通信装置由红外发射管和红外接受管组成,红外发射管是能发射出红外线的发光二极管,发射强度随着电流的增大而增大;红外接受管是一个具有红外光敏感特征的PN节的光敏二极管,只对红外线有反应,产生光电流。

红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出。

通信方式:单工,异步;
红外LED波长:940nm;
通信协议标准:NEC标准;
遥控器:
在这里插入图片描述

2. 红外遥控系统

红外遥控系统一般由红外发射装置、红外接收设备两大部分组成。

2.1 红外发射

红外发射功能主要由红外发射管来实现,红外发射管在外观上和透明的LED发光二极管极为相似,其驱动和控制方式也一致。在使用单片机控制发射管时,一般使用三极管来驱动,NPN三极管和PNP三极管都可以实现。PNP三极管的基极通过电阻接单片机的GPIO口,发射管通过限流电阻接在PNP三极管的发射极上。当单片机的GPIO输出高电平时PNP三极管处于截止状态红外发射管不工作;当GPIO输出低电平时PNP三极管导通发射管工作,发出肉眼不可见的红外线,被接收管接收到。

具体电路有以下两种:
在这里插入图片描述

2.2 红外接收

红外接收管在外观上类似为黑色LED发光二极管,在没有接收到红外信号时,接收管不导通,三极管不导通,单片机接收到持续的高电平;当接收管接收到红外信号时,单片机接收到低电平。当遥控器的按键被按下时,按键对应的编码脉冲就会被单片机所接收到,单片机解析该脉冲,就能知道遥控器上是哪个按键被按下,从而实现用户的操作。但是,黑色的红外接收管抗干扰能力比较低,在设计电路的时候一般不选用,而是选用专用的红外接收头,最常用的型号为HS0038。而且,其红外接收电路简单,抗干扰能力强。
在这里插入图片描述
具体电路图:
在这里插入图片描述
接收头会对接收到的红外光进行一定的过滤工作,然后输出到P32引脚上。

3. 红外调制

红外遥控发射数据时采用调制的方式,即把数据和一定频率的载波进行“与”操作,这样可以提高发射效率和降低电源功耗。
调制载波频率一般在30khz到60khz之间,大多数使用的是38kHz,占空比1/3的方波,如下图(载波波形)是由发射端所使用的455kHz晶振决定的。在发射端要对晶振进行整数分频,分频系数一般取12,所以455kHz÷12≈37.9 kHz≈38kHz。
在这里插入图片描述

基本发送与接收

红外LED的三种发送状态下接收头的输出:
①空闲状态:红外LED不亮,接收头输出高电平;
②发送低电平:红外LED以38KHz频率闪烁发光,接收头输出低电平;
③发送高电平:红外LED不亮,接收头输出高电平。

例如:
在这里插入图片描述
在这里插入图片描述
每个部分1个字节(8位),而第一和第二,第三和第四个部分互为反码是为了进行数据的校验信号的三种情况。

接收到的信号分为以下三种情况:
① 信息头,图中的红色信号,提示将要发送信号
② 信息体,图中的蓝色信号,真正需要传输的内容
③ 重复信号,图中的绿色信号,代表前面发送的内容重复发送

二、中断系统

中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。

当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中段源。

STC89C52系列单片机的各个中断查询次序如下表所示:

在这里插入图片描述
STC89C52系列单片机的中断系统结构示意图如下图所示:
在这里插入图片描述

STC89C51RC系列单片机各个中断触发行为总结如下表所示:
在这里插入图片描述
STC89C52系列单片机中断相关的所有寄存器如下表所示:
在这里插入图片描述

本节我主要对外部中断0和定时器0进行简单的介绍与应用

1. 外部中断0 与 定时器中断0 简介

外部中断0(INT0)既可低电平触发,也下降沿触发。请求中断的标志位是位于寄存器TCON中的IE0 / TCON.1。当外部中断服务程序被响应后,中断请求标志位IE0会自动被清0。TCON寄存器中的IT0/TCON.0决定了外部中断0是低电平触发方式还是下降沿触发方式。如果IT0=0,那么系统在INT0脚探测到低电平后可产生外部中断。如果IT0=1,那么系统在INT0脚探测下降沿后可产生外部中断。外部中断0(INT0)还可以用于将单片机从掉电模式唤醒。

定时器0中断请求标志位是TF0。当定时器寄存器TH0/TL0溢出时,溢出标志位TF0会被置位,定时器中断发生。当单片机转去执行该定时器中断时,定时器的溢出标志位TF0会被硬件清除。

2. 相关寄存器介绍

  1. IE 中断允许控制寄存器
    在这里插入图片描述
    说明:
    EA :全局中断允许位,当此位是1时中断可用
    ET2:定时器/计数器2中断允许位
    ES :串口中断允许位
    ET1:定时器/计数器1中断允许位
    EX1:外部中断1允许位
    ET0:定时器/计数器0中断允许位
    EX0:外部中断0允许位

  2. TCON 控制寄存器
    在这里插入图片描述
    说明:
    TF1 :定时器1溢出标志位
    TR1 :定时器1运行控制位
    TF0 :定时器0溢出标志位
    TR0: 定时器0运行控制位
    IE1 :外部中断1请求标志 IE1=1则外部中断1在向CPU请求中断,当CPU响应中断时硬件清0。一般不用手动设置。
    IT1 :外部中断1触发方式选择位 该位为0时INT1引脚上的低电平信号可触发外部中断1。该位为1时INT1引脚上的负跳变信号可触发外部中断1。
    IE0 :外部中断0请求标志 IE0=1则外部中断0在向CPU请求中断,当CPU响应中断时硬件清0。一般不用手动设置。
    IT0 :外部中断0触发方式选择位 该位为0时INT0引脚上的低电平信号可触发外部中断1。该位为1时INT1引脚上的负跳变信号可触发外部中断1。

  3. TMOD寄存器
    在这里插入图片描述
    说明:
    GATE :定时操作开关控制位,当GATE=1时,INT0或INT1引脚为高电平,同时TCON中的TR0或TR1控制位为1时,计时/计数器0或1才开始工作。若GATE=0,则只要将TR0或TR1控制位设为1,计时/计数器0或1就开始工作。
    C/T :定时器或计数器功能的选择位。C/T=1为计数器,通过外部引脚T0或T1输入计数脉冲。C/T=0时为定时器,由内部系统时钟提供计时工作脉冲。
    M1 、M0:T0、T1工作模式选择位
    在这里插入图片描述

三、代码实现

本文利用的是模块化编程
代码如下(示例):

1.延迟模块

delay.c

void delay(unsigned int xms)
{unsigned char i, j;while(xms--){i = 2;j = 239;do{while (--j);} while (--i);}
}

delay.h

#ifndef ___delay_H__
#define ___delay_H__
void delay(unsigned int xms);#endif

2. 定时器模块

2.1 定时器0

Time0.c

#include <reg52.h>void Timer0_Init(void)
{TMOD &= 0xF0;		//设置定时器模式TMOD |= 0x01;		//设置定时器模式TL0 = 0;		//设置定时初值TH0 = 0;		//设置定时初值TF0 = 0;		//清除TF0标志TR0 = 0;		//定时器0不计时
}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;
}

Time0.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

2.2 定时器1

time1.c

#include<reg52.h>void Timer1Init()		//100微秒 @11.0592MHz
{TMOD &= 0xF0;		//设置定时器模式TMOD |= 0x01;		//设置定时器模式TL1 = 0xA4;		//设置定时初值TH1 = 0xFF;		//设置定时初值TF1 = 0;		//清除TF0标志TR1 = 1;		//定时器0开始计时ET1=1;EA=1;PT1=0;}

time1.h

#ifndef ___time1_H__
#define ___time1_H__
void Timer1Init();#endif

3. 数码管模块

smg.c

#include<reg52.h>
#include "delay.h"
#define led P0
sbit P24=P2^4;
sbit P23=P2^3;
sbit P22=P2^2;//数码管段码表
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};void Nixie(unsigned char Location,Number)
{switch(Location)		//位码输出{case 1:P24=1;P23=1;P22=1;break;case 2:P24=1;P23=1;P22=0;break;case 3:P24=1;P23=0;P22=1;break;case 4:P24=1;P23=0;P22=0;break;case 5:P24=0;P23=1;P22=1;break;case 6:P24=0;P23=1;P22=0;break;case 7:P24=0;P23=0;P22=1;break;case 8:P24=0;P23=0;P22=0;break;}P0=NixieTable[Number];	//段码输出delay(1);				//显示一段时间led=0x00;				//段码清0,消影
}

smg.h

#ifndef __NIXIE_H__
#define __NIXIE_H__void Nixie(unsigned char Location,Number);#endif

4. 独立按键模块

key.c

#include<reg52.h>
#include "delay.h"sbit P30=P3^0;
sbit P31=P3^1;
sbit P32=P3^2;
sbit P33=P3^3;unsigned char key()
{unsigned char KeyNumber=0;if(P31==0){delay(20);while(P31==0);delay(20);KeyNumber=1;}if(P30==0){delay(20);while(P30==0);delay(20);KeyNumber=2;}if(P32==0){delay(20);while(P32==0);delay(20);KeyNumber=3;}if(P33==0){delay(20);while(P33==0);delay(20);KeyNumber=4;}return KeyNumber;
}

key.h

#ifndef ___key_H__
#define ___key_H__unsigned char key();#endif

5. 外部中断0模块

lnt0.c

#include <reg52.h>void Int0_Init(void)
{IT0=1;IE0=0;EX0=1;EA=1;PX0=1;
}

lnt0.h

#ifndef __INT0_H__
#define __INT0_H__void Int0_Init(void);#endif

6. 红外遥控模块

IR.c

#include <reg52.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;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>12442-500 && IR_Time<12442+500){IR_State=2;			//置状态为2}//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)else if(IR_Time>10368-500 && IR_Time<10368+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>1032-500 && IR_Time<1032+500){IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));	//数据对应位清0IR_pData++;			//数据位置指针自增}//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)else if(IR_Time>2074-500 && IR_Time<2074+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

7. 主函数模块

car.c

#include<reg52.h>
#include"delay.h"
#include"smg.h"
#include"key.h"
#include"time0.h"
#include"IR.h"sbit p17=P1^7;     //两个电机
sbit p16=P1^6;     
sbit p15=P1^5;
sbit p14=P1^4;unsigned int t,Speed,Command;
unsigned char Compare;
int main()
{    IR_Init();Timer1Init();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){Compare=0;}	//速度输出if(Speed==1){Compare=30;}if(Speed==2){Compare=70;}if(Speed==3){Compare=100;}}Nixie(7,Speed);						//数码管显示速度//			p17 = 0;        //左轮正转 
//			p16 = 1;
//    	  	p17 = 1;        //反转 
//      	p16 = 0; 
//			p17 = 0;         //停止
//			p16 = 0;
//			p15 = 1;         //右轮正转
//			p14 = 0;
//			p15 = 0;         //反转
//			p14 = 1;   
//			p15 = 0;         //停止
//			p14 = 0;}}void Timer0() interrupt 3
{static unsigned int i;TH1=0xff;           TL1=0xa4;  i++;i%=100;if(i<Compare){p17 = 1;        //反转 p16 = 0;   p15 = 0;         //反转p14 = 1;}else{p17 = 0;         //停止p16 = 0;p15 = 0;         p14 = 0;}
}

总结

本节是以STC89C52单片机为CPU,通过一些外围电路和软件编程实现小车红外遥控调速的功能。整个设计过程中最大的特点是利用简单的理论原理将红外遥控模块、L298N驱动模块、51单片机这三个模块有效的结合起来,利用红外遥控原理与pwm调节占空比的简单结合实现对小车红外遥控调速奠定编程理论基础,提高了效率,降低了编程的复杂度,具有很强的研究的意义,智能化的发展促使了智能小车往功能更加强大的方向发展。


http://www.ppmy.cn/news/140982.html

相关文章

C51(蓝牙,红外)智能遥控小车

目录 1模块准备 2实现功能 1模块准备 三轮小车底盘 l298n电机驱动模块 51最小系统开发板 ds1302时钟模块 蓝牙串口模块hc-06 18650电池盒和电池组三节&#xff0c;12v左右 ds18b20温度传感器 红外接收头与遥控 lcd1602显示屏 ttl电机2只 2实现功能 小车由蓝牙模块和红…

基于STM32智能小车蓝牙遥控实验(有代码含上位机)

小车设计 利用STM32作为智能小车的主控制器来驱动智能小车的直流电机工作&#xff0c;电机驱动芯片采用L298N微型集成电路电机驱动芯片&#xff0c;配合STM32核心板使用实现四个直流电机运行和pwm软件调速&#xff0c;通过改变直流电机占空比的电压来改变平均电压的数值&#…

智能红外遥控器(一):功能简介

开发板功能模块如下图&#xff1a; 红外遥控器有红外收发和温湿度读取功能&#xff0c;配合ESP32模块&#xff08;型号为ESP-WROOM-32&#xff09;可实现蓝牙&WIFI远程控制家中的红外电器。 欢迎大家加入远望创客学堂QQ群&#xff0c;一起学习新知识。删除& —等特殊字…

最简单DIY蓝牙PS2遥控器控制蓝牙智能小车

51单片机物联网智能小车系列文章目录 第一篇&#xff1a;最简单DIY的51蓝牙遥控小车设计方案 第二篇&#xff1a;最简单DIY串口蓝牙硬件实现方案 第三篇&#xff1a;最简单DIY蓝牙PS2遥控器控制蓝牙智能小车 文章目录 51单片机物联网智能小车系列文章目录前言一、最简单DIY蓝牙…

室内也能用 那些适合在跑步机上使用的可穿戴设备

来源&#xff1a;腾讯数码 冬天来了&#xff0c;很多人都不喜欢到寒冷的户外去跑步&#xff0c;尤其是晚上&#xff0c;想要夜跑更是一种严峻的挑战。 当你开始为智能手表或蓝牙耳机不适合为自己找借口、或者对寒冷的冬天心有余悸的话&#xff0c;完全可以选择在室内温暖的健身…

蓝牙遥控机械臂小车实现移动和搬运的功能

1. 功能描述 本实验所实现的功能为&#xff1a;用手机APP遥控轮式机械臂小车样机实现移动和搬运。 通过手机APP遥控轮式机械臂小车样机完成将工作区外的工件搬运至工作区的任务&#xff0c;来模拟机器人的搬运过程。首先手机遥控机器人找到未存放在工作区内的工件&#xff0c;然…

红外遥控Arduino智能小车

智能小车将会关于遥控、寻迹、避障的Arduino小车系列&#xff0c;有需要可以进入我的个人头像查看。 在智能小车项目中&#xff0c;我们通过控制直流电机的正反转、刹车和转速来控制小车的行动。 【iMake】Arduino入门自学&#xff0c;电子学会机器人等级考试三四级&#xff0…

【开发心得】一招减少msdtc时间3分钟,但还有未解之谜

最近解决了一个诡异的问题&#xff0c;MSDTC默认超时造成事务被取消&#xff0c;业务被迫中断&#xff0c;好在没有一直跟MSDTC耗着&#xff0c;而是通过其他方式解决了&#xff0c;但最后还是留下了两个未解之谜。对用到MSDTC处理SQL事务的朋友应该有借鉴作用&#xff0c;欢迎…