这是我上个月做的单片机智能小车,目前可实现红外避障和蓝牙遥控。保证原创,请多多指教。
单片机源程序如下:
- #include<reg52.h>
- #include<C52_car.h>
- #include <intrins.h>//包含_nop_指令的头文件
- #define MAIN_Fosc 11059200UL //宏定义主时钟HZ
- #define PCF8591_ADDR 0x90 //PCF8591地址
- #define DACOUT_EN 0x40 //DAC输出使能
- typedef unsigned char uchar;
- typedef unsigned int uint;
- unsigned char pwm_left_val = 0;//左电机占空比值 取值范围0-80,0最快
- unsigned char pwm_right_val = 0;//右电机占空比值取值范围0-80 ,0最快
- unsigned char pwm_t = 0;//周期
- unsigned char control=0X01;//车运动控制全局变量,默认开机为停车状态
- unsigned int time = 0;//传输时间
- unsigned long S = 0;//距离
- unsigned char count=0;
- unsigned char SEH_count; //舵机朝向前面
- #define LCD1602_DB P0 //LCD1602数据总线
- uchar Control_mode = 0;
- uchar AD_Value; //存储AD转换回的数字量
- sbit LCD1602_RS = P3^5; //RS端
- sbit LCD1602_RW = P3^6; //RW端
- sbit LCD1602_EN = P3^4; //EN端
- sbit DU = P2^6;//
- sbit WE = P2^7;//数码管位选段选用于关闭数码管显示
- void delay(unsigned int z)//毫秒级延时
- {
- unsigned int x,y;
- for(x = z; x > 0; x--)
- for(y = 114; y > 0 ; y--);
- }
- void Delay10us(unsigned char i) //10us延时函数 启动超声波模块时使用
- {
- unsigned char j;
- do{
- j = 10;
- do{
- _nop_();
- }while(--j);
- }while(--i);
- }
- /*====================================
- 函数:void Delay5us()
- 描述:12T 51单片机5微秒延时函数自适应时钟(11.0592M,12M,22.1184M)
- ====================================*/
- void Delay5us()
- {
- #if MAIN_Fosc == 11059200
- _nop_();
- #elif MAIN_Fosc == 12000000
- _nop_();
- #elif MAIN_Fosc == 22118400
- _nop_(); _nop_(); _nop_();
- #endif
- }
- /*小车前进*/
- void forward()
- {
- left_motor_go; //左电机前进
- right_motor_go; //右电机前进
- }
- /*小车左转*/
- void left_run()
- {
- left_motor_stops; //左电机停止
- right_motor_go; //右电机前进
- }
- /*小车右转*/
- void right_run()
- {
- right_motor_stops;//右电机停止
- left_motor_go; //左电机前进
- }
- /*PWM控制使能 小车后退*/
- void backward()
- {
- left_motor_back; //左电机后退
- right_motor_back; //右电机后退
- }
- void stop()
- {
- left_motor_stops; //左电机后退
- right_motor_stops; //右电机后退
- }
- void Init(void)
- {
- EA = 1; //开总中断
- PT0 = 1;
- IT0 = 0; //边沿触发方式
- IT1 = 0; //边沿触发方式
- SCON |= 0x50; // SCON: 模式1, 8-bit UART, 使能接收
- T2CON |= 0x34; //设置定时器2为串口波特率发生器并启动定时器2
- TL2 = RCAP2L = (65536-(FOSC/32/BAUD)); //设置波特率
- TH2 = RCAP2H = (65536-(FOSC/32/BAUD)) >> 8;
- ES= 1; //打开串口中断
-
- TMOD |= 0x01; //定时器0,工作模式1,16位定时模式
- TH0 = 0;
- TL0 = 0; //T0,16位定时计数用于记录ECHO高电平时间
- TR0 = 1; //启动定时器0
- ET0 = 1; //允许定时器0中断
- TMOD |= 0x10; //定时器1,16位定时模式。
- TH1 = 0xff; //配置定时器0初值,溢出时间为0.1ms
- TL1 = 0xa3;
- TR1 = 1; //启动定时器1
- ET1 = 1; //允许T1中断
- }
- void StartModule() //启动超声波模块
- {
- TX=1; //启动一次模块
- Delay10us(2);
- TX=0;
- }
- /*计算超声波所测距离并显示*/
- void Conut()
- {
- time=TH0*256+TL0;
- TH0 = 0;
- TL0 = 0;
- S=(float)(time*1.085)*0.17; //算出来是MM
- if(S>=7000) //超出测量范围
- {
- stop();
- beep = 0;
- delay(1000);
- beep = 1;
- }
- }
- /*====================================
- 函数:void SEH_count_0()
- 描述:舵机在左侧时指令
- ====================================*/
- void SEH_count_5()
- {
- StartModule(); //启动模块测距
- while(!RX); //当RX(ECHO信号回响)为零时等待
- TR0 = 1; //开启计数
- while(RX) ; //当RX为1计数并等待
- TR0 = 0; //关闭计数
- Conut(); //计算距离
- if(S > 170 && S <850 )//设置随动距离(单位毫米)
- {
- left_run();
- }
- }
- /*====================================
- 函数:void SEH_count_10()
- 描述:舵机在中间时指令
- ====================================*/
- void SEH_count_10()
- {
- StartModule(); //启动模块测距
- while(!RX); //当RX(ECHO信号回响)为零时等待
- TR0 = 1; //开启计数
- while(RX) ; //当RX为1计数并等待
- TR0 = 0; //关闭计数
- Conut(); //计算距离
- if(S > 170 && S <850 )//设置随动距离(单位毫米)
- {
- forward();
- }
- }
- /*====================================
- 函数:void SEH_count_20()
- 描述:舵机在右侧时指令
- ====================================*/
- void SEH_count_15()
- {
- StartModule(); //启动模块测距
- while(!RX); //当RX(ECHO信号回响)为零时等待
- TR0 = 1; //开启计数
- while(RX) ; //当RX为1计数并等待
- TR0 = 0; //关闭计数
- Conut(); //计算距离
- if(S > 150 && S <850 )//设置随动距离(单位毫米)
- {
- right_run();
- }
- }
- /*====================================
- 函数:I2C_init()
- 描述:I2C总线初始化
- ====================================*/
- void I2C_init()
- {
- SDA = 1; //数据总线高
- _nop_();
- SCL = 1; //时钟总线高
- _nop_();
- }
- /*====================================
- 函数:I2C_Start()
- 描述:I2C起始信号
- ====================================*/
- void I2C_Start()
- {
- SCL = 1;
- _nop_();
- SDA = 1;
- Delay5us();
- SDA = 0;
- Delay5us();
- }
- /*====================================
- 函数:I2C_Stop()
- 描述:I2C停止信号
- ====================================*/
- void I2C_Stop()
- {
- SDA = 0;
- _nop_();
- SCL = 1;
- Delay5us();
- SDA = 1;
- Delay5us();
- }
- /*====================================
- 函数:Master_ACK(bit i)
- 参数:i 为0时发送非应答 为1时发送应答
- 描述:I2C主机发送应答
- ====================================*/
- void Master_ACK(bit i)
- {
- SCL = 0; // 拉低时钟总线允许SDA数据总线上的数据变化
- _nop_(); // 让总线稳定
- if (i) //如果i = 1 那么拉低数据总线 表示主机应答
- {
- SDA = 0;
- }
- else
- {
- SDA = 1; //发送非应答
- }
- _nop_();//让总线稳定
- SCL = 1;//拉高时钟总线 让从机从SDA线上读走 主机的应答信号
- _nop_();
- SCL = 0;//拉低时钟总线, 占用总线继续通信
- _nop_();
- SDA = 1;//释放SDA数据总线。
- _nop_();
- }
- /*====================================
- 函数:Test_ACK()
- 返回:0为非应答 1为应答
- 描述:I2C检测从机应答
- ====================================*/
- bit Test_ACK() // 检测从机应答
- {
- SCL = 1;//时钟总线为高电平期间可以读取从机应答信号
- Delay5us();
- if (SDA)
- {
- SCL = 0;
- I2C_Stop();
- return(0);
- }
- else
- {
- SCL = 0;
- return(1);
- }
- }
- /*====================================
- 函数:I2C_send_byte(uchar byte)
- 参数:byte 要发送的字节
- 描述:I2C发送一个字节
- ====================================*/
- void I2C_send_byte(uchar byte)
- {
- uchar i;
- for(i = 0 ; i < 8 ; i++)
- {
- SCL = 0;
- _nop_();
- if (byte & 0x80) //
- {
- SDA = 1;
- _nop_();
- }
- else
- {
- SDA = 0;
- _nop_();
- }
- SCL = 1;
- _nop_();
- byte <<= 1;
- }
- SCL = 0;
- _nop_();
- SDA = 1;
- _nop_();
- }
- /*====================================
- 函数:I2C_read_byte()
- 返回:读取的字节
- 描述:I2C读一个字节
- ====================================*/
- uchar I2C_read_byte()
- {
- uchar i, dat;
- SCL = 0 ;
- _nop_();
- SDA = 1;
- _nop_();
- for(i = 0 ; i < 8 ; i++)
- {
- SCL = 1;
- _nop_();
- dat <<= 1;
- if (SDA)
- {
- dat |= 0x01;
- }
- _nop_();
- SCL = 0;
- _nop_();
- }
- return(dat);
- }
- /*读AD数据*/
- bit ADC_Read(uchar CON)
- {
- I2C_Start();
- I2C_send_byte(PCF8591_ADDR+0);
- if (!Test_ACK())
- {
- return(0);
- }
- I2C_send_byte(CON);
- Master_ACK(0);
- I2C_Start();
- I2C_send_byte(PCF8591_ADDR+1);
- if (!Test_ACK())
- {
- return(0);
- }
- AD_Value = I2C_read_byte();
- Master_ACK(0);
- I2C_Stop();
- return(1);
- }
- /*=================================================
- *函数名称:Read_Busy
- *函数功能:判断1602液晶忙,并等待
- =================================================*/
- void Read_Busy()
- {
- uchar busy;
- LCD1602_DB = 0xff;//复位数据总线
- LCD1602_RS = 0; //拉低RS
- LCD1602_RW = 1; //拉高RW读
- do
- {
- LCD1602_EN = 1;//使能EN
- busy = LCD1602_DB;//读回数据
- LCD1602_EN = 0; //拉低使能以便于下一次产生上升沿
- }while(busy & 0x80); //判断状态字BIT7位是否为1,为1则表示忙,程序等待
- }
- /*=================================================
- *函数名称:LCD1602_Write_Cmd
- *函数功能:写LCD1602命令
- *调用:Read_Busy();
- *输入:cmd:要写的命令
- =================================================*/
- void LCD1602_Write_Cmd(uchar cmd)
- {
- Read_Busy(); //判断忙,忙则等待
- LCD1602_RS = 0;
- LCD1602_RW = 0; //拉低RS、RW操作时序情况1602课件下中文使用说明基本操作时序章节
- LCD1602_DB = cmd;//写入命令
- LCD1602_EN = 1; //拉高使能端 数据被传输到LCD1602内
- LCD1602_EN = 0; //拉低使能以便于下一次产生上升沿
- }
- /*=================================================
- *函数名称:LCD1602_Write_Dat
- *函数功能:写LCD1602数据
- *调用:Read_Busy();
- *输入:dat:需要写入的数据
- =================================================*/
- void LCD1602_Write_Dat(uchar dat)
- {
- Read_Busy();
- LCD1602_RS = 1;
- LCD1602_RW = 0;
- LCD1602_DB = dat;
- LCD1602_EN = 1;
- LCD1602_EN = 0;
- }
- /*=================================================
- *函数名称:LCD1602_Dis_Str
- *函数功能:在指定位置显示字符串
- *调用:LCD1602_Write_Cmd(); LCD1602_Write_Dat();
- *输入:x:要显示的横坐标取值0-40,y:要显示的行坐标取值0-1(0为第一行,1为第二行)
- *str:需要显示的字符串
- =================================================*/
- void LCD1602_Dis_Str(uchar x, uchar y, uchar *str)
- {
- if(y) x |= 0x40;
- x |= 0x80;
- LCD1602_Write_Cmd(x);
- while(*str != '\0')
- {
- LCD1602_Write_Dat(*str++);
- }
- }
- /*=================================================
- *函数名称:Init_LCD1602
- *函数功能:1602初始化
- *调用: LCD1602_Write_Cmd();
- =================================================*/
- void Init_LCD1602()
- {
- LCD1602_Write_Cmd(0x38); // 设置16*2显示,5*7点阵,8位数据接口
- LCD1602_Write_Cmd(0x0c); //开显示
- LCD1602_Write_Cmd(0x06); //读写一字节后地址指针加1
- LCD1602_Write_Cmd(0x01); //清除显示
- }
- /*=================================================
- *函数名称:Dispaly_LCD1602
- *函数功能:1602显示字符
- *调用: LCD1602_Write_Cmd();
- =================================================*/
- void Dispaly_LCD1602()
- {
- int LCD_CK=0;
- uchar Str_1[] = {"Welcome to use"};
- uchar Str_2[] = {"Pick a pattern"};
- uchar Str_3[] = {"S2 -> automatic"};
- uchar Str_4[] = {"S3 -> bluetooth"};
- Init_LCD1602();//1602初始化
- for(;;) //死循环
- {
- if(LCD_CK == 0)
- {
- LCD1602_Dis_Str(0, 0, &Str_1[0]); //显示字符串
- LCD1602_Dis_Str(0, 1, &Str_2[0]); //显示字符串
- delay(10);
- }
- if(LCD_CK == 20000)
- {
- LCD1602_Dis_Str(0, 0, &Str_3[0]); //显示字符串
- LCD1602_Dis_Str(0, 1, &Str_4[0]); //显示字符串
- delay(10);
- }/**/
-
- if(key_s2 == 0)// 实时检测S2按键是否被按下
- {
- delay(5); //软件消抖
- if(key_s2 == 0)//再检测S2是否被按下
- {
- while(!key_s2);//松手检测
- delay(50);//50毫秒延时
- Control_mode = 0;
- break; //退出FOR死循环
- }
- }
- if(key_s3 == 0)// 实时检测S3按键是否被按下
- {
- delay(5); //软件消抖
- if(key_s3 == 0)//再检测S3是否被按下
- {
- while(!key_s3);//松手检测
- delay(50);//50毫秒延时
- Control_mode = 1;
- break; //退出FOR死循环
- }
- }
- if(LCD_CK <= 40000) LCD_CK++;
- if(LCD_CK >= 40000) LCD_CK = 0;
- }
- }
- void main()
- {
- DU = 0;
- WE = 0;
- EN2 = EN1 =1;
- SEH_count = 10;
- Dispaly_LCD1602();
- I2C_init();//I2C初始化
- LCD1602_Write_Cmd(0x08); //关闭1602显示
- Init();//定时器、串口初始化Init();//定时器、串口初始化
- beep = 0;
- delay(200);//延时1秒
- beep = 1;
- while(1)
- {
- if(Control_mode == 1)
- {
- EX0 = 0; //关闭外部中断1
- EX1 = 0; //关闭外部中断2
- while(1)
- {
- if(control>0X07)//如果成立,则表示接收的命令不在运行命令内
- {
- stop(); // 停车
- }
- switch(control)
- {
- case 0X02: forward(); break;//前进
- case 0X03: backward(); break;//后退
- case 0X04: left_run(); break;//左转
- case 0X05: right_run(); break;//右转
- case 0X01: stop(); break;//停车
- case 0X08: beep = 0; break;//鸣笛
- case 0X09: beep = 1; break;//停止鸣笛
- case 0X0B: SEH_count = 10; break;//SEH_COUNT: 10:0.5+1(0.1*10T)=1.5ms->90°舵机角度
- case 0X0A: Control_mode = 0;break;
- }
- break;
- }
- }
- if(Control_mode == 0)
- {
- EX0 = 1; //打开外部中断1
- EX1 = 1; //打开外部中断2
- for (;;)
- {
- SEH_count = 12;
- delay(300);
- ADC_Read(0x03);//体感模块,当检测到人体收输出3.3高电平
- if(AD_Value > 100) SEH_count_10();
- SEH_count = 6;
- delay(300);
- ADC_Read(0x03);//体感模块,当检测到人体收输出3.3高电平
- if(AD_Value > 100) SEH_count_5();//大于3.1V表示感应到了人体,小车行进
- SEH_count = 12;
- delay(300);
- ADC_Read(0x03);//体感模块,当检测到人体收输出3.3高电平
- if(AD_Value > 100) SEH_count_10();
- SEH_count = 18;
- delay(300);
- ADC_Read(0x03);//体感模块,当检测到人体收输出3.3高电平
- if(AD_Value > 150) SEH_count_15();
- if(control == 0X0C)
- {
- Control_mode = 1;
- break;
- }
- }
- }
- }
- }
- void int0() interrupt 0
- {
- EX0 = 0;
- delay(10);
- if(left_led2 == 0)
- {
- backward();
- delay(500);
- key_s3 = 1;
- right_run();
- delay(700);
- forward();
- delay(500);
- stop();
- }
- EX0 = 1;
- }
- //定时器0中断
- void timer0() interrupt 1
- {
- /* pwm_t++;//周期计时加
- if(pwm_t == 120)
- pwm_t = EN1 = EN2 = 0;
- if(pwm_left_val == pwm_t)//左电机占空比
- EN1 = 1;
- if(pwm_right_val == pwm_t)//右电机占空比
- EN2 = 1;
- */
- }
- void int1() interrupt 2
- {
- EX1 = 0;
- delay(10);
- if(right_led2 == 0)
- {
-
- backward();
- delay(500);
- key_s3 = 1;
- left_run();
- delay(700);
- forward();
- delay(500);
- stop();
- }
- EX1 = 1;
- }
- //定时器1中断
- void timer1() interrupt 3 //T1中断用来计数器溢出
- {
- TR1 = 0; //关闭定时器1
- TH1 = 0xff; //重装初值0.1ms
- TL1 = 0xa3;
- //舵机1
- if(count <= SEH_count) //控制占空比左右
- {
- //如果count的计数小于(5-25)也就是0.5ms-2.5ms则这段小t周期持续高电平。产生方波
- Servo = 1;
- }
- else
- {
- Servo = 0;
- }
- count++;
- if (count >= 200) //T = 20ms则定时器计数变量清0
- {
- count = 0;
- }
- TR1 = 1; //开启定时器1
- }
- /******************************************************************/
- /* 串口中断程序*/
- /******************************************************************/
- void UART_SER () interrupt 4
- {
- unsigned char n; //定义临时变量
- if(RI) //判断是接收中断产生
- {
- RI=0; //标志位清零
- n=SBUF; //读入缓冲区的值
- control=n;
- /* if((n >= 51) && (n <= 150))//左电机调速0~100个档位 手机端软件进行调节
- pwm_left_val = 0.8-((n-50)*0.8);
- if((n >= 151) && (n <= 250)) //右电机调速0~100个档位 手机端软件进行调节
- pwm_right_val = 0.8-((n-150)*0.8);*/
- if((n >= 51) && (n <= 150))//左电机调速0~100个档位 手机端软件进行调节
- pwm_left_val = 1.7-((n-50)*1.7);
- if((n >= 151) && (n <= 250)) //右电机调速0~100个档位 手机端软件进行调节
- pwm_right_val = 1.7-((n-150)*1.7);
- }
- }
复制代码
所有资料51hei提供下载:
Smart_car4 红外避障&超声波跟随.rar
(56.44 KB, 下载次数: 25)
|