智能汽车(1)-汽车的前进、后退和停止 智能车(二)-------- 红外遥控调速 智能车(三)-------- 汽车的红外跟踪
文章目录
- 前言
- 一、红外遥控
-
- 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台单片机下智能汽车的简单应用,在上一节智能汽车(1)的基础上增加了一些功能,如:通过调整电机旋转的比例,实现汽车的简单性pwm调速;在调速的基础上,增加红外遥控模块,实现汽车红外遥控调速或增加红外循环模块。之后,我将带您逐步进入51台单片机的具体应用。
介绍相关硬件模块,请前往上一节 “ 智能车(1) ”
一、红外遥控
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不亮,接收头输出高电平。
例如: 每个部分有三种情况:字节(8位),第一部分和第二部分,第三部分和第四部分相反,以验证数据信号。
接收到的信号分为以下三种情况: ① 信息头,图中的红色信号,提示将发送信号 ② 信息体,图中的蓝色信号,真正需要传输的内容 ③ 重复信号,图中的绿色信号,代表前面发送的内容
二、中断系统
中断系统是为使CPU具有实时处理外部紧急事件的能力。
中央处理机CPU在处理某件事时,外界发生了紧急要求,要求CPU暂停当前工作,转而处理紧急情况。处理完毕后,回到原来中断的地方,继续原来的工作。这个过程被称为中断。实现此功能的部件称为中断系统,请指示CPU中断请求源称为中段源。
STC89C如下表所示:
STC89C52系列单片机中断系统结构示意图如下图所示:
STC89C51RC总结如下表所示: STC89C所有与52系列单片机中断相关的寄存器如下表所示:
本节主要简要介绍和应用外部中断0和定时器0
1. 外部中断0 与 定时器中断0 简介
外部中断0(INT0)既能低电平触发,又能沿触发下降。请求中断的标志位是位于寄存器TCON中的IE0 / TCON.1。响应外部中断服务程序后,中断请求标志位IE0会自动被清除0。TCON寄存器中的IT0/TCON.0决定了外部中断0是低电平触发还是下降沿触发。如果IT0=所以系统在INT在检测到低电平后,0脚会产生外部中断。如果IT0=所以系统在INT0脚探测下降沿后可产生外部中断。外部中断0(INT0)也可用于唤醒单片机从脱电模式。
定时器0中断请求标志位TF0.定时器寄存器TH0/TL0溢出时,溢出标志位TF0将被定位,定时器中断。当单片机转向执行定时器中断时,定时器的溢出标志位置TF硬件将清除0。
2. 相关寄存器介绍
-
IE 中断允许控制寄存器 说明: EA :全局中断允许位,当此位为1时中断 ET2:定时器/计数器2中断允许位置 ES :串口中断允许位置 ET1:定时器/计数器1中断允许位置 EX1:允许外部中断1 ET0:定时器/计数器0中断允许位置 EX0:允许外部中断0
-
TCON 控制寄存器 说明: TF1 :定时器1溢出标志位 TR1 :定时器1运行控制位 TF0 :定时器0溢出标志位 TR0: 定时器0操作控制位 IE1 :外部中断1请求标志 IE1=外部中断1在向CPU请求中断,当CPU当响应中断时,硬件清除0。一般不需要手动设置。 IT1 :外部中断1触发法选择位置 该位为0时INT引脚上的低电平信号可触发外部中断1。这个位置是1点INT引脚上的负跳变信号可触发外部中断1。 IE0 :外部中断0请求标志 IE0=外部中断0在向CPU请求中断,当CPU当响应中断时,硬件清除0。一般不需要手动设置。 IT0 :外部中断0触发法选择位置 该位为0时INT0引脚上的低电平信号可触发外部中断1。这个位置是1点INT引脚上的负跳变信号可触发外部中断1。
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); //定时计数器清0 Timer0_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; //置收到连发帧标志位为1 Timer0_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)); //数据对应位清0 IR_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)); //数据对应位置1 IR_pData++; //数据位置指针自增 } else //接收出错 { IR_pData=0; //数据位置指针清0 IR_State=1; //置状态为1 } if(IR_pData>=32) //如果接收到了32位数据 { IR_pData=0; //数据位置指针清0 if((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 0x4A
void IR_Init(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAddress(void);
unsigned