标题:
智能小车学习进修 激光引导 循迹 遥控原理图与程序
[打印本页]
作者:
木子灵晋
时间:
2017-7-8 21:25
标题:
智能小车学习进修 激光引导 循迹 遥控原理图与程序
希望能得到学习与交流,大家有什么意见可以一起交流,含有我做的几个项目的完整原理图与单片机源码.
0.png
(44.12 KB, 下载次数: 105)
下载附件
2017-7-9 04:53 上传
单片机源程序如下:
//******小车程序******
#include"stc12c5a.h"
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
uchar sys_mod = 't';//'t':循迹模式, 'r':遥控模式, 'l',激光引导模式
//*******NRF24L01******
uchar idata RxBuf[16]; //接收缓存 data区满 存入idata区
uchar idata TxBuf[16]; //发送缓存
//******0:系统模式-- ‘t’:循迹模式,‘r’:遥控模式,'l':激光模式
//******1: 时间高8位
//******2: 时间低8位
//******3: ADC4
//******4: ADC5
//******5: 感光板上最小电压值
//******6: 感光板上激光打到的位置
//******7: 行驶步骤高8位
//******8: 行驶步骤低8位
//******9: 硬币数量
//*****10: 激光模块上步进电机转过的角度高8位
//*****11: 激光模块上步进电机转过的角度低8位
//*****12: addone变量 NRF24L01每发送一次数据,addone自加1,接收方判断发送方是否离线的一个标志
//addone在一定时间内都保持不变说明发送方离线
//*****13:未定义
#define LED P36 //led灯接P3.6
#define SPK P37 //蜂鸣器接P3.7
//*********** LCD ************
#define RS P44
#define RW P45
#define LCDEN P46
#define LCD_DATA P0 //P0口接LCD数据口
#define LCD_BUSY P07 //lcd1602忙碌标志位
uchar idata lcd_code[10];//用来标记lcd1602 什么时候清显示 每个页面都设一个code,code不想同时清显示
//********** 红外探头及行进方式宏定义 ***************
#define PROBE_L P22 //左侧红外探头 白色区域低电平 黑色高电平
#define PROBE_M P21 //中间红外探头
#define PROBE_R P20 //右侧红外探头
#define LEFT_Z P30 //左轮正极
#define LEFT_F P31 //左轮负极
#define RIGHT_Z P34 //右轮正极
#define RIGHT_F P35 //右轮负极
#define LEFT_GO { LEFT_Z = 1;LEFT_F = 0;} //左轮向前转
#define LEFT_BACK { LEFT_Z = 0;LEFT_F = 1;} //左轮向后转
#define LEFT_STOP { LEFT_Z = 0;LEFT_F = 0;} //左轮停止
#define RIGHT_GO { RIGHT_Z = 1;RIGHT_F = 0;} //右轮向前转
#define RIGHT_BACK { RIGHT_Z = 0;RIGHT_F = 1;} //右轮向后转
#define RIGHT_STOP { RIGHT_Z = 0;RIGHT_F = 0;} //右轮停止
//****************************************************************
//uchar left_mod[5] = "STOP"; //左轮转动方式 用于在lcd上显示
//uchar right_mod[5] = "STOP"; //右轮转动方式 用于在lcd上显示
uchar probe_before = 's'; //上一次探头的状态 's':停止 'l':左转 'g':前进 'r':右转
uchar step = 1; //第?步 0表示检测到硬币
uchar num_coin = 0; //硬币数量
uchar see_coin = 0; //0:未检测到硬币 1:检测到硬币
uchar flag_see_coin = 0; //0:刚检测到硬币 1:不是刚检测到 刚检测到硬币要为t1_flag_see_coin 置零
uchar change_line = 1; //进入内圈的步骤
uchar times_probe_l = 0; //左探头探测到黑带的次数
uchar times_probe_r = 0; //右探头探测到黑带的次数
uchar change_line_mod = '?';//进入内圈的方式 是左转还是右转
#define xs 8 //系数,1表示占空比再乘以0.1,设置合适 <=8 可安全使用12V的电池
//**************** ADC *********************
uchar idata adc5[49]; //data区满 存到idata
uchar times_adc = 0; //adc转换完成的次数
uchar num_vmin = 0; //电压最小的光敏电阻的编号 0~48(横向加1 纵向加7)
uchar num_vmin_before = 24; //激光脱离前打到的点 第3行 第3列为停止的点
uint volt = 0;
//*********** CD4051通道 *********
#define SW_CH P23 //行扫描a b c (选通行接地)
#define SW_BH P24
#define SW_AH P25
#define SW_CL P26 //列扫描a b c (选通列输出电压值给ADC)
#define SW_BL P27
#define SW_AL P32
//***************************************
uint angle = 0; //步进电机转过的角度
//******************* 定时器变量 ***********************
uint t0_flag = 0; //定时器0进入中断的次数
uint t1_flag = 0; //定时器1进入中断的次数
uint t1_flag_add = 0; //为addone = addone_b 定时,即为与发送端失联时间定时
uint t1_flag_time = 0; //系统时间中断次数 1000次为1s
uint t1_flag_see_coin = 0; //用于在检测到硬币时 停车2秒 和声光报警功能
uint t1_flag_laserout = 0; //为激光脱离感光板定时
uchar flag_laserout = 0; //标记是否刚脱离 0:刚脱离,1:已经脱离 刚脱离置零t1_flag_laserout
uchar t0_h;
uchar t0_l;
uchar t1_h;
uchar t1_l;
uint time_of_move = 0; //小车移动总时间 单位s
//*****************************************延时函数***************************************
void delayms(uint ms)//延时?个 ms
{
unsigned char a,b;
while(ms--)
{
for(b=64;b>0;b--) // 仅作为粗略延时 中断繁忙时差距很大
for(a=45;a>0;a--);
}
}
void delayus(uint us)
{
for(;us >0;us--)
{
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); //stc12c5a不分频 12MHz 下12个指令周期为1us
}
}
//**********字符串复制函数**********
void string_copy(uchar *target,uchar *source)//字符串复制 target:目标 source:源
{
uchar i = 0;
for(i = 0;source[i] != '\0';i++)//注意target的长度 无保护措施!
{
target[i] = source[i];
}
target[i] = '\0';
}
//**********字符串比较函数**********
uchar string_cmp(uchar *target,uchar *source)//字符串比较 target:目标 source:源
{
uchar revalue;
uchar i = 0;
for(i = 0;target[i] != '\0' && source[i] != '\0';i++) //两个都不等于'\0'才执行 出现一个等于'\0'就跳出
{
if(target[i] == source[i])
{
revalue = 1;
}
else
{
revalue = 0;
break;
}
}
if(revalue == 1)
{
if(target[i] == '\0' && source[i] == '\0')
revalue = 1;
else
revalue = 0;
}
return(revalue);
}
//****************************************NRF24L01 IO端口定义*********************************
#define CE P10
#define SCK P11
#define MISO P12
#define CSN P16
#define MOSI P17
//#define IRQ P32 //以下程序IRQ不显示中中断
//*****************************NRF24L01的接收和发送地址***************************************
#define TX_ADR_WIDTH 5 // 5个字节的TX地址长度
#define RX_ADR_WIDTH 5 // 5个字节的RX地址长度
#define TX_PLOAD_WIDTH 16 // ?个字节的TX数据长度
#define RX_PLOAD_WIDTH 16 // ?个字节的RX数据长度
uchar const TX_ADDRESS[TX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //本地地址
uchar const RX_ADDRESS[RX_ADR_WIDTH]= {0x34,0x43,0x10,0x10,0x01}; //接收地址
//***************************************NRF24L01寄存器指令**********************************
#define READ_REG 0x00 // 读寄存器
#define WRITE_REG 0x20 // 写寄存器
#define RD_RX_PLOAD 0x61 // 读取接收数据
#define WR_TX_PLOAD 0xA0 // 写待发数据
#define FLUSH_TX 0xE1 // 冲洗发送 FIFO
#define FLUSH_RX 0xE2 // 冲洗接收 FIFO
#define REUSE_TX_PL 0xE3 // 定义重复装载数据
#define NOP 0xFF // 保留
//*************************************SPI(nRF24L01)寄存器地址*****************************
#define CONFIG 0x00 // 配置收发状态,CRC校验模式以及收发状态响应方式
#define EN_AA 0x01 // 自动应答功能设置
#define EN_RXADDR 0x02 // 可用信道设置
#define SETUP_AW 0x03 // 收发地址宽度设置
#define SETUP_RETR 0x04 // 自动重发功能设置
#define RF_CH 0x05 // 工作频率设置
#define RF_SETUP 0x06 // 发射速率、功耗功能设置
#define STATUS 0x07 // 状态寄存器
#define OBSERVE_TX 0x08 // 发送监测功能
#define CD 0x09 // 地址检测
#define RX_ADDR_P0 0x0A // 频道0接收数据地址
#define RX_ADDR_P1 0x0B // 频道1接收数据地址
#define RX_ADDR_P2 0x0C // 频道2接收数据地址
#define RX_ADDR_P3 0x0D // 频道3接收数据地址
#define RX_ADDR_P4 0x0E // 频道4接收数据地址
#define RX_ADDR_P5 0x0F // 频道5接收数据地址
#define TX_ADDR 0x10 // 发送地址寄存器
#define RX_PW_P0 0x11 // 接收频道0接收数据长度
#define RX_PW_P1 0x12 // 接收频道0接收数据长度
#define RX_PW_P2 0x13 // 接收频道0接收数据长度
#define RX_PW_P3 0x14 // 接收频道0接收数据长度
#define RX_PW_P4 0x15 // 接收频道0接收数据长度
#define RX_PW_P5 0x16 // 接收频道0接收数据长度
#define FIFO_STATUS 0x17 // FIFO栈入栈出状态寄存器设置
uchar SPI_RW_Reg(uchar reg, uchar value);
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars);
//****************************************状态标志****************************************
uchar bdata sta;
sbit RX_DR =sta^6;
sbit TX_DS =sta^5;
sbit MAX_RT =sta^4;
//********************************NRF24L01初始化******************************************
void init_NRF24L01()
{
delayus(100);
CE=0; // 片选使能
CSN=1; // SPI使能
SCK=0; // SPI时钟拉低
SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH); //写本地地址
SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); //写接收端地址
SPI_RW_Reg(WRITE_REG + EN_AA, 0x01); //通道0自动应答
SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01); //允许接收地址频道0
SPI_RW_Reg(WRITE_REG + RF_CH, 0x32); //设置信道工作频率,收发必须一致
SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度
SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x0f); //设置发射速率为2MHZ,发射功率为最大值0dB
SPI_RW_Reg(WRITE_REG + CONFIG, 0x7c); //IRQ引脚不显示中断 掉电模式 1~16位CRC校验
}
//****************************************************************************************************
//*函数:uint SPI_RW(uint uchar)
//*功能:NRF24L01的SPI写时序
//****************************************************************************************************
uchar SPI_RW(uchar num)
{
uchar bit_ctr;
for(bit_ctr=0;bit_ctr<8;bit_ctr++) // output 8-bit
{
MOSI = (num & 0x80); // output 'uchar', MSB to MOSI
num = (num << 1); // shift next bit into MSB..
SCK = 1; // Set SCK high..
num |= MISO; // capture current MISO bit
SCK = 0; // ..then set SCK low again
}
return(num); // return read uchar
}
//****************************************************************************************************
//函数:uchar SPI_Read(uchar reg)
//功能:NRF24L01的SPI时序
//****************************************************************************************************
uchar SPI_Read(uchar reg)
{
uchar reg_val;
CSN = 0; // CSN low, initialize SPI communication...
SPI_RW(reg); // Select register to read from..
reg_val = SPI_RW(0); // ..then read registervalue
CSN = 1; // CSN high, terminate SPI communication
return(reg_val); // return register value
}
//****************************************************************************************************
//*功能:NRF24L01读写寄存器函数
//****************************************************************************************************
uchar SPI_RW_Reg(uchar reg, uchar value)
{
uchar status;
CSN = 0; // CSN low, init SPI transaction
status = SPI_RW(reg); // select register
SPI_RW(value); // ..and write value to it..
CSN = 1; // CSN high again
return(status); // return nRF24L01 status uchar
}
//****************************************************************************************************
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
uchar status,byte_ctr;
CSN = 0; // Set CSN low, init SPI tranaction
status = SPI_RW(reg); // Select register to write to and read status byte
for(byte_ctr=0;byte_ctr<bytes;byte_ctr++)
pBuf[byte_ctr] = SPI_RW(0); // Perform SPI_RW to read byte from nRF24L01
CSN = 1; // Set CSN high again
return(status); // return nRF24L01 status byte
}
//*********************************************************************************************************
//*函数:uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)
//*功能: 用于写数据:为寄存器地址,pBuf:为待写入数据地址,uchars:写入数据的个数
//*********************************************************************************************************
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars)
{
uchar status,uchar_ctr;
CSN = 0; //SPI使能
status = SPI_RW(reg);
for(uchar_ctr=0; uchar_ctr<uchars; uchar_ctr++) //
SPI_RW(*pBuf++);
CSN = 1; //关闭SPI
return(status);
}
//********** nrf收发程序 **********
void nrf_RxTx(uchar mod_nrf,uchar *buff) //NRF24L01收发程序
{
static uchar mod_nrf_b;//static 地址不释放
//******进入发射模式******
if(mod_nrf == 't')
{
if(mod_nrf_b != 't')
{
mod_nrf_b = 't';
CE = 0;
SPI_RW_Reg(WRITE_REG+STATUS,0xff); //清除中断标志
SPI_RW_Reg(FLUSH_TX,0x00); //清除TX_FIFO寄存器
SPI_RW_Reg(WRITE_REG + CONFIG,0x7e);//IRQ引脚不显示中断 上电 发射模式 1~16CRC校验
CE = 1;
delayus(130);//从CE = 0 到 CE = 1;即待机模式到收发模式,需要最大130us
}
//******发送数据******
CE = 0; //StandBy I模式
SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // 装载接收端地址
SPI_Write_Buf(WR_TX_PLOAD,buff,TX_PLOAD_WIDTH); // 装载数据
CE = 1; //置高CE激发数据发送
delayus(130);//从CE = 0 到 CE = 1;即待机模式到收发模式,需要最大130us
delayus(100); //给发送数据一点时间 发送延时可以比接收少
sta = SPI_Read(STATUS);//读取状态寄存器的值
SPI_RW_Reg(WRITE_REG+STATUS,sta);//清除对应的中断
if(TX_DS == 1)//发送成功再清除tx fifo寄存器
{
CE = 0;
SPI_RW_Reg(FLUSH_TX,0x00); //清除tx fifo寄存器 //********重要*********
CE = 1;
}
}
//******进入接收模式******
else if(mod_nrf == 'r')//接收模式
{
if(mod_nrf_b != 'r')
{
mod_nrf_b = 'r';
CE = 0;
SPI_RW_Reg(WRITE_REG+STATUS,0xff); //清除中断标志
SPI_RW_Reg(FLUSH_RX,0x00); //清除RX_FIFO寄存器
SPI_RW_Reg(WRITE_REG + CONFIG, 0x7f);//IRQ引脚不显示中断 上电 接收模式 1~16CRC校验
CE = 1;
delayus(130);//从CE = 0 到 CE = 1;即待机模式到收发模式,需要最大130us
}
delayus(500); //不能少 值可调 给接收数据一点时间
sta = SPI_Read(STATUS);
SPI_RW_Reg(WRITE_REG+STATUS,sta);
if(RX_DR == 1)
{
CE = 0;
SPI_Read_Buf(RD_RX_PLOAD,buff,RX_PLOAD_WIDTH);//读取数据 存入数组
SPI_RW_Reg(FLUSH_RX,0x00);//清除rx fifo寄存器 数据不抖动
CE = 1;
}
}
}
//************* 外部中断 *********************
void int1() interrupt 2 //检测到硬币时发生中断 P3.3引脚
{
while(1)//检测到硬币时,即时冲出赛道也能重新回去
{
if(PROBE_L == 1 && PROBE_M == 0 && PROBE_R == 0)
{ probe_before = 'l';break; }
else if(PROBE_L == 0 && PROBE_M ==0 && PROBE_R == 1)
{ probe_before = 'r';break; }
}
EX1 = 0; //禁止中断 等到P33电平由0变1时再允许中断
see_coin = 1;
num_coin++;
}
//******************** 定时器 ***********************
void timer_init()//定时器初始化
{
// ET0 = 1;
ET1 = 1;
TMOD = 0x11;// 定时器0,1 :不响应外部引线,定时器方式(为振荡器12分频计数) 模式1(16位计数器)
TCON = 0;
// t0_h = 0x3C; //0x3CB0 = 65536 -50000= 15536 50ms中断一次
// t0_l = 0xB0;
// TH0 = t0_h;
// TL0 = t0_l;
t1_h = 0xFC;
t1_l = 0x18;//0xFC18 = 64536 1000 = 65536 - 64536 1ms中断一次
TH1 = t1_h;
TL1 = t1_l;
}
/*
//**********定时器0**********
void timer0() interrupt 1 //定时器默认12T 即12MHz晶振下 THi TLi 每1us加1,与c51单片机兼容
{
TH0 = t0_h; //0x3CAF = 65535 -50000= 15535 50ms中断一次
TL0 = t0_l;
t0_flag++;
if(t0_flag == 20) //1s
{
t0_flag = 0;
time_of_move++;//每过一秒 小车行驶时间变量+1
}
}*/
//**********定时器1**********
void timer1() interrupt 3 //每1ms中断一次
{
TH1 = 0xFC;
TL1 = 0x17;
t1_flag++;
t1_flag_add++;
t1_flag_time++;
t1_flag_see_coin++;
t1_flag_laserout++;
if(t1_flag_time == 1000)
{
t1_flag_time = 0;
time_of_move++;//每过一秒 小车行驶时间变量+1
}
}
//************* PWM调速相关函数 ****************
void pca0()//P1.3输出
{
CCAPM0 = 0x42;//8位PWM,无中断模式
CCAP0H = CCAP0L = 0xFF;//初始占空比设为0%
}
void pca1()//P1.4输出
{
CCAPM1 = 0x42;
CCAP1H = CCAP1L = 0xFF;
}
void pca_start()//设置pca工作模式
{
CMOD = 0x02;//2分频(23kHz在人耳听觉范围之外),不允许CF中断 允许中断时,运行卡
CCON = 0;
CH = CL = 0;
CR = 1;//pca开始计数
}
void pwm(unsigned long pwm0,unsigned long pwm1)
{
uint pwm0_temp,pwm1_temp;
pwm0_temp = 255 - (pwm0*xs*255/1000); //pwm0 = 0 时 pwm0_temp = 255;xs:系数0~10
pwm1_temp = 255 - (pwm1*xs*255/1000);
CCAP0H = CCAP0L = pwm0_temp;
CCAP1H = CCAP1L = pwm1_temp;
}
//**************** LCD1602 ********************
//******LCD基本函数******
void busy_check() //忙碌检测
{
RW = 1; //读
RS = 0; //指令寄存器
LCD_DATA = 0xFF;//实验证明读数时要将I/O口要置1
LCDEN = 0;
_nop_();
_nop_();
_nop_();
_nop_();
LCDEN = 1;// EN高电平读信息 负跳变执行指令
_nop_();
_nop_();
_nop_();
_nop_();
while(1)
{
if(LCD_BUSY == 0)//P07 == 0跳出循环
break;
}
}
void lcdwrcom(uchar command)//写指令
{
busy_check();
RW = 0;//写
RS = 0;//指令寄存器
LCD_DATA = command;
LCDEN = 1;//负跳变写入指令
_nop_();
_nop_();
_nop_();
_nop_();
LCDEN = 0;
}
void lcdwrdata(uchar lcd_data)//写数据 数字、字母、标点符号都是数据
{
busy_check();
RW = 0;//写
RS = 1;//数据寄存器
LCD_DATA = lcd_data;
LCDEN = 1;//负跳变写入指令
_nop_();
_nop_();
_nop_();
_nop_();
LCDEN = 0;
}
void lcd_init()
{
delayms(20);//必要 lcd1602上电到电压稳定需要时间
RW = 0;//写
RS = 0;//指令寄存器
LCD_DATA = 0x38;// 0x38设置显示模式为:16X2 显示,5X7 点阵,8 位数据接口'
LCDEN = 1;
_nop_();
_nop_();
_nop_();
_nop_();
LCDEN = 0;
delayms(5);
lcdwrcom(0x0c);//打开显示 无光标 不闪烁
lcdwrcom(0x06);//指令3 光标右移 屏幕所有文字移动无效
lcdwrcom(0x01);// 清显示,光标复位到地址00H位置。
}
//****** LCD扩展函数 ******
void address(uchar x,uchar y) //定位下一步要写数的地址
{
uchar location;
if(x == 0)
location = 0x80|y;
else
location = 0xC0|y;
lcdwrcom(location);
}
void printchar(uchar x,uchar y,uchar letter)//显示字母、单个字符
{
address(x,y);
lcdwrdata(letter);
}
void printword(uchar x,uchar y,uchar *word) //显示单词(字符数组)
{
uchar i = 0;
for(i = 0;word[i] != '\0';i++)
{
address(x,y + i);
lcdwrdata(word[i]);
}
}
void printuint(uchar x,uchar y,uchar num_ws_max,uint num)//显示无符号整形 0~65535 x:行 y:列 num_ws_max 变量的最大位数
{
uchar i = 0;
uchar str[5] = {0x20,0x20,0x20,0x20,0x20};
if(num >= 10000)
{
str[0] = num/10000 + '0';
str[1] = num%10000/1000 + '0';
str[2] = num%1000/100 + '0';
str[3] = num%100/10 + '0';
str[4] = num%10 + '0';
// str[5] = '\0'; //手动加字符串结束标志
}
else if(num >= 1000)
{
str[0] = num/1000 + '0';
str[1] = num%1000/100 + '0';
str[2] = num%100/10 + '0';
str[3] = num%10 + '0';
str[4] ='\0';
}
else if(num >= 100)
{
str[0] = num/100 + '0';
str[1] = num%100/10 + '0';
str[2] = num%10 + '0';
str[3] = '\0';
}
else if(num >=10)
{
str[0] = num/10 + '0';
str[1] = num%10 + '0';
str[2] = '\0';
}
else if(num >= 0)
{
str[0] = num + '0';
str[1] = '\0';
}
for(i = 0;i <= 5;i++) //uint类型 最大值65535 为5位数
{
if(str[i] != '\0' && i < num_ws_max)
{
address(x,y + i);
lcdwrdata(str[i]);
}
else if(str[i] == '\0' && i < num_ws_max)
{
address(x,y+i);
lcdwrdata(' ');//空格 // 实现功能:在此变量的位数范围内,把没数字的位存0x20(空白)
//例如:最大有3位:999 当变为99时,存为9+'0' 9+'0' 0x20
}
}
}
/*
void printvolt(uchar x,uchar y,uint v) //将一个3位数除以100 保留两位小数 可用来显示电压 0.00~5.00V
{
uchar str[4] = "0";
uchar i = 0;
str[0] = v/100 + '0';
str[1] = '.';
str[2] = v%100/10 + '0';
str[3] = v%10 + '0';
printword(x,y,str);
}*/
//*************** ADC ******************
void adc_init() //adc初始化
{
EADC = 1; //允许ADC中断
SW_CH = SW_BH = SW_AH = 0;
SW_CL = SW_BL = SW_AL = 0;
ADC_CONTR = 0x8D;//使用P1.5口 540个时钟周期转换一次
// ADC_CONTR = 0xCD;//使用P1.5口 180个时钟周期转换一次
P1ASF = 0x20;//P1口 P1.5作为模拟功能A/D使用
}
void adc() interrupt 5
{
unsigned long int volt_temp; //涉及到*500的计算 所以定义出无符号长整形
uchar i = 0;
// ADC_CONTR = 0xC5;// A/D转换完成 ADC_FLAG软件置零 停止转换
ADC_CONTR = 0x85;
adc5[times_adc] = ADC_RES;
volt_temp = ADC_RES;
volt = volt_temp*500/255; //volt_temp 设为无符号长整形 先乘再除保证精度。将0~5的数拉长为0~500,提高精度。
if(times_adc < 48 )
{
times_adc++; //0~48
SW_CH = 4&(times_adc/7); //按位与 接通cd4051正确的行
SW_BH = 2&(times_adc/7);
SW_AH = 1&(times_adc/7);
SW_CL = 4&(times_adc%7); //接通cd4051正确的列
SW_BL = 2&(times_adc%7);
SW_AL = 1&(times_adc%7);
}
else //找出最小值,当最小值小于某个值说明激光打在adc_min这个区域
{
times_adc = 0;
SW_CH = SW_BH = SW_AH = 0;
SW_CL = SW_BL = SW_AL = 0; //为下一次扫描cd4051做准备
//***** 判断最小值 ******
num_vmin = 0;
for(i = 0;i < 49;i++)
{
if(adc5[i] <= adc5[num_vmin])
num_vmin = i;
}
}
// ADC_CONTR = 0xCD;//开始转换
ADC_CONTR = 0x8D;
}
void move(uchar g_b_l,uchar g_b_r,uchar pwm0,uchar pwm1)
//g_b_l: 'g'左轮前进 'b' 左轮后退 ;g_b_r:'g':右轮前进 'b'右轮后退 pwm_left:左轮占空比 pwm_right:右轮占空比
{
if(g_b_l == 'g')
{
LEFT_GO;
/* left_mod[0] = 'G'; //数组只能在初始化时整体赋值 在这里left[] = "GO "是行不通的
left_mod[1] = 'O';
left_mod[2] = 0x20; //空值 打空格也行
left_mod[3] = ' '; //空格
left_mod[4] = '\0'; */
}
else if(g_b_l == 'b')
{
LEFT_BACK;
/* left_mod[0] = 'B';
left_mod[1] = 'A';
left_mod[2] = 'C';
left_mod[3] = 'K';
left_mod[4] = '\0'; */
}
else if(g_b_l == 's')
{
LEFT_STOP;
/* left_mod[0] = 'S';
left_mod[1] = 'T';
left_mod[2] = 'O';
left_mod[3] = 'P';
left_mod[4] = '\0'; */
}
if(g_b_r == 'g')
{
RIGHT_GO;
/* right_mod[0] = 'G';
right_mod[1] = 'O';
right_mod[2] = 0x20; //空值
right_mod[3] = 0x20; //空值
right_mod[4] = '\0'; */
}
else if(g_b_r == 'b')
{
RIGHT_BACK;
/* right_mod[0] = 'B';
right_mod[1] = 'A';
right_mod[2] = 'C';
right_mod[3] = 'K';
right_mod[4] = '\0';*/
}
else if(g_b_r == 's')
{
RIGHT_STOP;
/* right_mod[0] = 'S';
right_mod[1] = 'T';
right_mod[2] = 'O';
right_mod[3] = 'P';
right_mod[4] = '\0'; */
}
if(pwm0 >= 0 && pwm0 <= 100 && pwm1 >= 0 && pwm1 <= 100)
{
pwm(pwm0,pwm1);
}
else //值不在0 ~100 的范围
{
pwm(0,0);
}
}
void track(uchar pwm0,uchar pwm1)//循迹函数
{
if(PROBE_L == 0 && PROBE_M ==0 && PROBE_R == 0) //处理上一步探头状态 避免冲出跑道
{
if(probe_before == 'l') //上一步左转
{
move('b','g',86,86);//左轮?马力向后转 右轮?马力向前转 即左转
}
else if(probe_before == 'g') //上一步前进
{
move('g','g',pwm0,pwm1);//左右轮分别以pwm0 pwm1 马力前进
// move('b','g',pwm0,pwm1);//左轮pwm0马力向后转 右轮pwm1马力向前转 即左转
//继续前进的话容易出现失误,跑出赛道。选择左转,右转均可
}
else if(probe_before == 'r') //上一步右转
{
move('g','b',86,86);//左轮?马力向前转 右轮?马力向后转 即右转
}
}
else if(PROBE_L == 0 && PROBE_M == 1 && PROBE_R == 0) //中部探头在黑带处 前进
{
probe_before = 'g';//前进标志
move('g','g',pwm0,pwm1);//前进
}
else if(PROBE_L == 1 && PROBE_M == 1 && PROBE_R == 0)
{
if(change_line_mod == '?')
{
times_probe_l++;
}
probe_before = 'l'; //左转标志
move('b','g',85,85);//左转
}
else if(PROBE_L == 1 && PROBE_M ==0 && PROBE_R == 0)
{
if(change_line_mod == '?')
{
times_probe_l++;
}
probe_before = 'l'; //左转标志
move('b','g',86,86);//左转
}
else if(PROBE_L == 0 && PROBE_M == 1 && PROBE_R == 1)
{
if(change_line_mod == '?') //未定值时
{
times_probe_r++;
}
probe_before = 'r';//右转标志
move('g','b',85,85);//右转
}
else if(PROBE_L == 0 && PROBE_M ==0 && PROBE_R == 1)
{
if(change_line_mod == '?') //未定值时
{
times_probe_r++;
}
probe_before = 'r';//右转标志
move('g','b',86,86);//右转
}
else if(PROBE_L == 1 && PROBE_M == 1 && PROBE_R == 1) //赛道中 111表示横着的黑带,标志着下一个步骤
{
while(1)
{
move('g','g',pwm0,pwm1);//前进
if(PROBE_L == 0 || PROBE_M == 0 || PROBE_R == 0)
{
if(step < 255) //防止溢出变为0
step++;
break;
}
}
}
/* else if(PROBE_L == 1 && PROBE_M ==0 && PROBE_R == 1) //赛道上不出现这个情况
{
}
*/
if(change_line_mod == '?') //为change_line_mod的定值
{
if(times_probe_l == 15 || times_probe_r == 15) //任意一个探测到黑带数达到15 开始确定change_line_mod的值
{
if(times_probe_l > times_probe_r)
change_line_mod = 'l'; //左转进入内圈
else
change_line_mod = 'r'; //右转进入内圈
}
}
}
void track_all(uchar pwm_l,uchar pwm_r)
{
if(angle < 720)
{
/* if(P33 == 1 && EX1 == 0 && flag_see_coin == 0)
EX1 = 1;*/
if(see_coin == 1 && P33 == 1) //检测到硬币 且探头脱离硬币 防止多次检测同一个硬币
{
move('s','s',0,0);//停车
if(flag_see_coin == 0) //刚检测到硬币时 置零t1_flag_see_coin 开始计时
{
flag_see_coin = 1; //已经检测到硬币
t1_flag_see_coin = 0; //置零t1_flag_see_coin 开始计时
}
if(t1_flag_see_coin < 500)
{ LED = 1; SPK = 1; }
else if(t1_flag_see_coin < 1000)
{ LED = 0; SPK = 0; }
else if(t1_flag_see_coin < 1500)
{ LED = 1; SPK = 1; }
else if(t1_flag_see_coin < 2000)
{ LED = 0; SPK = 0; }
else
{
EX1 = 1;
see_coin = 0; //回去执行检测到硬币之前的else语句
flag_see_coin = 0;
}
}
else if(step == 0)
{
if(PROBE_L == 1 && PROBE_M == 1 && PROBE_R == 1)
{
move('g','g',83,83);
while(1)
{
if(PROBE_L == 0 || PROBE_M == 0 || PROBE_R == 0)
{
step = 1;
break;
}
}
}
}
else if(step == 1)
{
track(pwm_l,pwm_r);
}
else if(step == 2)
{
track(pwm_l + 5,pwm_r + 5); //加速行驶
}
else if( step == 3)
{
track(pwm_l,pwm_r);
}
else if(step == 4) //进入内圈 声光提示
{
if(change_line == 1)//第1步 驶出横黑带
{
LED = SPK = 1;
move('g','g',pwm_l,pwm_r);
if(PROBE_L == 0 || PROBE_M == 0 || PROBE_R == 0)
change_line = 2;
}
else if(change_line == 2)//第2步 让与转向方式相异的边探头探测到黑带
{
LED = SPK = 0;
if(change_line_mod == 'l') //左转 探头状态001 (让右探头探测黑带,再进入下一步)
{
move('b','g',86,86);
if(PROBE_L == 0 && PROBE_M == 0 && PROBE_R == 1)
change_line = 3;
}
else if(change_line_mod == 'r') //右转 探头状态100
{
move('g','b',86,86);
if(PROBE_L == 1 && PROBE_M == 0 && PROBE_R == 0)
change_line = 3;
}
}
else if(change_line == 3) //第3步 转向前进交替
{
if(change_line_mod == 'l') //左转前进交替
{
LED = SPK = 1;
move('b','g',86,86);
delayms(10);
LED = SPK = 0;
move('g','g',pwm_l + 15,pwm_r + 15);
delayms(5);
if(PROBE_L == 1 || PROBE_M == 1)
{
LED = SPK = 0;
change_line = 0;
step = 5;
}
}
else if(change_line_mod == 'r') //右转前进交替
{
LED = SPK = 1;
move('g','b',86,86);
delayms(10);
LED = SPK = 0;
move('g','g',pwm_l + 15,pwm_r + 15);
delayms(5);
if(PROBE_L == 1 || PROBE_M == 1)
{
LED = SPK = 0;
change_line = 0;
step = 5;
}
}
}
}
else
{
track(pwm_l,pwm_r);
}
}
else if(angle >= 720)
{
move('s','s',0,0);
}
}
void laser_guide(uchar pwm) //激光引导模式
{
uchar x;
uchar y;
if(adc5[num_vmin] < 210)// 确保有激光打到光敏电阻上才执行
{
flag_laserout = 0;
x = num_vmin/7; //对7取整得到行数
y = num_vmin%7; //对7取余得到列数
num_vmin_before = num_vmin;
if(x >= 2 && x <= 4 && y <= 2)
{
move('g','g',pwm - 5,pwm - 5);//前进 转弯比直走费劲
}
else if(x >= 2 && x <= 4 && y >= 4)
{
move('b','b',pwm - 5,pwm - 5);//后退
}
else if(x >= 5)
{
move('b','g',pwm,pwm);//左转 转弯比直走费劲
}
else if(x <= 2)
{
move('g','b',pwm,pwm);//右转
}
else
{
move('s','s',0,0);//停止
}
}
else //激光脱离 保持原行驶状态一段时间,希望能重新感应到
{
x = num_vmin_before/7; //对7取整 得到行数
y = num_vmin_before%7; //对7取余 得到列数
if(flag_laserout == 0) //刚脱离感光板
{
flag_laserout = 1;
t1_flag_laserout = 0; //置零 每1ms加1
}
if(t1_flag_laserout < 300 ) //?ms
{
if(x >= 2 && x <= 4 && y <= 2)
{
move('g','g',pwm - 5,pwm - 5);//前进
}
else if(x >= 2 && x <= 4 && y >= 4)
{
move('b','b',pwm - 5,pwm - 5);//后退
}
else if(x >= 5)
{
move('b','g',pwm,pwm);//左转
}
else if(x <= 2)
{
move('g','b',pwm,pwm);//右转
}
else
{
move('s','s',0,0);//停止
}
}
else
{
move('s','s',0,0);
num_vmin_before = 24;
}
}
}
//************ 判断NRF24L01是否联机 ************
uchar isonline(uint time) //返回0:离线、超时 ,返回1:在线或未超时
{
static uchar addone;
static uchar addone_b;
static uchar flag_add;
uchar revalue;
addone = RxBuf[12];
if(addone == addone_b) //值不变 发送方可能离线 当值保持不变的时间超过参数time,则视发送方为离线
{
if(flag_add == 0)
{
flag_add = 1;
t1_flag_add = 0;//t1_flag_add 置0;t1_flag_add在定时器1中断函数中 每1ms自加1
}
if(t1_flag_add < time)//未超时
{
revalue = 1;
}
else if(t1_flag_add >= time)//超时
{
t1_flag_add = time;
revalue = 0; //超时 发送端离线
}
}
else if(addone != addone_b)
{
addone_b = addone;
flag_add = 0;
revalue = 1;//值在变 发送端在线 未失联
}
return(revalue);//返回1:在线 , 0:离线
}
void rc_ol(uchar pwm_ctrl_l,uchar pwm_ctrl_r) //rc 遥控 模式 发送方在线
{
uchar mod_ctrl_l;
uchar mod_ctrl_ls; //用于显示+ -
uchar mod_ctrl_r;
uchar mod_ctrl_rs; //用于显示+ -
uint pwm_ctrl_temp; //防止数据溢出
if(pwm_ctrl_l >= 129 && pwm_ctrl_l <= 255)//129~255 差值126
{
mod_ctrl_l = 'g';
mod_ctrl_ls = '+';//前进
pwm_ctrl_temp = pwm_ctrl_l;
pwm_ctrl_l = (pwm_ctrl_temp - 129)*100/126;//将129~255转换成0~100
}
else if(pwm_ctrl_l >= 0 && pwm_ctrl_l <= 126)
{
mod_ctrl_l = 'b';
mod_ctrl_ls = '-';
pwm_ctrl_temp = pwm_ctrl_l;
pwm_ctrl_l = 100 - (pwm_ctrl_temp*100/126); //将0~126转换为100~0
}
else //包括等于 127,128 其他
{
mod_ctrl_l = 's';
mod_ctrl_ls = ' ';
pwm_ctrl_l = 0;
}
if(pwm_ctrl_r >= 129 && pwm_ctrl_r <= 255)//129~255 差值126
{
mod_ctrl_r = 'g';//前进
mod_ctrl_rs = '+';
pwm_ctrl_temp = pwm_ctrl_r;
pwm_ctrl_r = (pwm_ctrl_temp - 129)*100/126;//将129~255转换成0~100
}
else if(pwm_ctrl_r >= 0 && pwm_ctrl_r <= 126)
{
mod_ctrl_r = 'b';
mod_ctrl_rs = '-';
pwm_ctrl_temp = pwm_ctrl_r;
pwm_ctrl_r = 100 - (pwm_ctrl_temp*100/126); //将0~126转换为100~0
}
else //包括等于 127,128 其他
{
mod_ctrl_r = 's';
mod_ctrl_rs = ' ';
pwm_ctrl_r = 0;
}
move(mod_ctrl_l,mod_ctrl_r,pwm_ctrl_l,pwm_ctrl_r);
if(string_cmp(lcd_code,"rc_ol") != 1)
{
lcdwrcom(0x01);//清显示
string_copy(lcd_code,"rc_ol");
}
printword(0,0,"MODE:RC");
printword(0,8,"TIME:");
printuint(0,13,3,time_of_move);
printword(1,0,"L:");
printchar(1,2,mod_ctrl_ls);
printuint(1,3,3,pwm_ctrl_l);
printword(1,8,"R:");
printchar(1,10,mod_ctrl_rs);
printuint(1,11,3,pwm_ctrl_r);
}
//******************** 主函数 ***********************
void main()
{
uchar addone_tx = 0;
uchar pwm_ctrl_l;
uchar pwm_ctrl_r;
uint pwm_rx = 0;
uint pwm_laser = 0;
……………………
…………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
小车.zip
(107.05 KB, 下载次数: 57)
2017-7-8 21:24 上传
点击文件名下载附件
程序,原理图
下载积分: 黑币 -5
作者:
骨灰级新手
时间:
2018-5-17 19:02
谢谢分享
作者:
腾飞的龙
时间:
2019-8-30 09:08
很好的资料,感谢分享,学习啦!!!!!!!!
作者:
江辰子
时间:
2021-3-10 15:07
用的是什么传感器啊
作者:
770260686
时间:
2021-4-8 11:02
同问,用的是什么传感器
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1