-
Arduino智能小车——蓝牙小车
2019-05-20 10:25:11Arduino智能小车——蓝牙小车 上一章我们完成了小车的运动控制,虽然小车已经可以运动,但是不能远程遥控,不够高大上。在这一篇,我们将尝试用手机蓝牙遥控小车。蓝牙模块 蓝牙( Bluetooth® ):是一种无线...Arduino智能小车——蓝牙小车
Arduino智能小车系列教程时空门:
- Arduino智能小车——拼装篇 点击跳转
- Arduino智能小车——测试篇 点击跳转
- Arduino智能小车——调速篇 点击跳转
- Arduino智能小车——超声波避障 点击跳转
- Arduino智能小车——蓝牙小车 点击跳转
- Arduino智能小车——循迹篇 点击跳转
- Arduino智能小车——小车测速 点击跳转
上一章我们完成了小车的运动控制,虽然小车已经可以运动,但是不能远程遥控,不够高大上。在这一篇,我们将尝试用手机蓝牙遥控小车。
蓝牙模块
蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。
我们在此使用的蓝牙模块(HC-05)已经在内部实现了蓝牙协议,不用我们再去自己开发调试协议。这类模块一般都是借助于串口协议通信,因此我们只需借助串口将我们需要发送的数据发送给蓝牙模块,蓝牙模块会自动将数据通过蓝牙协议发送给配对好的蓝牙设备。
串口通信
由于要借助串口实现蓝牙通信功能,所以我们在此要先了解下Arduino的串口通信。
Arduino UNO开发板上的串口为0->RX,1->TX,在开发板内部也已经配置好了串口的功能,我们只需调用函数借口即可。以下列出串口通信里面常用的函数,并加以简单解释,详细用法可在用到时自行查询。
开启串行通信接口并设置通信波特率
Serial.begin(speed);
关闭串口通信
Serial.end();
判断串口缓冲器是否有数据写入
Serial.available();
读取串口数据
Serial.read();
返回下一字节(字符)输入数据,但不删除它
Serial.peek();
清空串口缓存
Serial.flush();
写入字符串数据到串口
Serial.print();
写入字符串数据+换行到串口
Serial.println();
写入二进制数据到串口
Serial.write();
read时触发的事件函数
Serial.SerialEvent();
读取固定长度的二进制流
Serial.readBytes(buffer,length);
打印接到数据十进制表示的ascii码
Serial.println(incomingByte, DEC);
蓝牙模块连接
TX: 接Arduino UNO开发板"RX"引脚
RX: 接Arduino UNO开发板"TX"引脚
GND: 接Arduino UNO开发板"GND"引脚
VCC: 接Arduino UNO开发板"5V"或"3.3V"引脚手机蓝牙助手
想实现手机蓝牙遥控小车,手机APP是必不可少的,目前网上有很多蓝牙串口助手,省去了我们自己写APP的时间,当然如果朋友你有能力或者想自己DIY的话也可以尝试自己写APP,在这里我推荐大家用这款手机蓝牙助手(百度上搜手机蓝牙串口助手就可以搜到,挺好用的)
下载并安装后打开APP,在这里可能会提示你有新版本请求更新,建议点击以后再说(暂时不更新),以我的经验,一般点击立即更新都会更新失败。
点击自定义按键[2],将其“显示名称”属性改为“前进”,“点击发送”属性改为“01”,并点击“确定”保存
点击自定义按键[4],将其“显示名称”属性改为“左转”,“点击发送”属性改为“03”,并点击“确定”保存
点击自定义按键[5],将其“显示名称”属性改为“后退”,“点击发送”属性改为“02”,并点击“确定”保存
点击自定义按键[6],将其“显示名称”属性改为“右转”,“点击发送”属性改为“04”,并点击“确定”保存以上修改的属性值即为我们点击对应按键之后,蓝牙串口助手自动通过蓝牙发送的数据,与上一篇所定义的小车的几个状态一致,这样方便在Arduino在接收到蓝牙模块的数据后对小车的状态进行控制。
#define STOP 0 #define FORWARD 1 #define BACKWARD 2 #define TURNLEFT 3 #define TURNRIGHT 4
修改后属性如下图
下面就来看看我们修改的效果吧,点击“模式切换”
小科普:
蓝牙模块上电(只简单连接"VCC"和"GND"引脚)之后,其他蓝牙设备即可与其连接,一般蓝牙模块默认初始连接密码为"0000"或"1234",如果连接不上蓝牙,请尽快与厂商或者店家联系。蓝牙模块上电后LED指示灯不断闪亮,当有设备连接预期连接之后会隔一段闪两下,蓝牙串口助手也会有相应已连接的提示。##Arduino代码编写
新建一个工程,将下面代码复制到工程内#define STOP 0 #define FORWARD 1 #define BACKWARD 2 #define TURNLEFT 3 #define TURNRIGHT 4 int leftMotor1 = 4; int leftMotor2 = 5; int rightMotor1 = 6; int rightMotor2 = 7; void setup() { // put your setup code here, to run once: Serial.begin(9600); pinMode(leftMotor1, OUTPUT); pinMode(leftMotor2, OUTPUT); pinMode(rightMotor1, OUTPUT); pinMode(rightMotor2, OUTPUT); } void loop() { // put your main code here, to run repeatedly: //usart read if(Serial.available()>0) { char cmd = Serial.read();//读取蓝牙模块发送到串口的数据 Serial.print(cmd); motorRun(cmd); } } void motorRun(int cmd) { switch(cmd){ case FORWARD: Serial.println("FORWARD"); //输出状态 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; case BACKWARD: Serial.println("BACKWARD"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNLEFT: Serial.println("TURN LEFT"); //输出状态 digitalWrite(leftMotor1, HIGH); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, HIGH); break; case TURNRIGHT: Serial.println("TURN RIGHT"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, HIGH); digitalWrite(rightMotor1, HIGH); digitalWrite(rightMotor2, LOW); break; default: Serial.println("STOP"); //输出状态 digitalWrite(leftMotor1, LOW); digitalWrite(leftMotor2, LOW); digitalWrite(rightMotor1, LOW); digitalWrite(rightMotor2, LOW); } }
朋友们大概也发现了,这个代码和上一篇的代码基本上相同,只不过增加了串口的内容。
##代码详解
串口初始化函数,想要通过串口的库函数对串口进行操作,必须在void set_up()
函数中对其进行初始化。Serial.begin(9600);
在void loop()函数内,加入了检测串口接收内容的函数,并将接收到的命令输入到 void motorRun(int cmd)函数中控制小车运动。
if(Serial.available()>0) { char cmd = Serial.read(); Serial.print(cmd); motorRun(cmd); }
蓝牙小车测试
下载程序之后,重新连接蓝牙模块,切换到我们自定义的按键界面,快试试蓝牙遥控小车吧。
附件
安卓手机蓝牙串口点击下载,也可以复制链接 https://download.csdn.net/download/qq_16775293/11165678 到浏览器下载。
欢迎各位有兴趣的朋友加入Q群1:789127261点评、交流
-
蓝牙小车
2019-01-26 22:27:05建立在51单片机以及stm32f4单片机上的蓝牙小车,它真的只是一台车没有什么别的功能。(主要原因是我太菜了) 硬件 淘宝搜索 1.蓝牙小车底盘。 2. l298n驱动两个。 3. 51单片机最小核心板。 4. STM32F4核心板...概要
建立在51单片机以及stm32f4单片机上的蓝牙小车,它真的只是一台车没有什么别的功能。(主要原因是我太菜了)
硬件
淘宝搜索
1.蓝牙小车底盘。
2. l298n驱动两个。
3. 51单片机最小核心板。
4. STM32F4核心板。
5. 杜邦线一大堆。
6. 口香糖电池。51单片机学习过程
买了一本郭天祥老师的51单片机教程以及一块学习板,配合着b站上的教程看,主要是为了初步了解入门单片机。怎么说呢,学习起来还是有点感悟的。
- 开始我是对照着书本进行学习,然后学了怎么操作自己试了一下之后就直接下一章节了,没有得到充分的练习,以至于真正做车车的时候发现有很多东西稀里糊涂的,得翻书重新查。这里的教训就是,学以致用很重要,理论-实践-理论的三明治原理。(后来发现郭天祥老师的视频里留有充足的课后练习)
- 其次就是中间中断了一段时间没有学习,大学生活丰富多彩嘛嘻嘻。。。没人管就很容易滋生堕落的情绪,所以说拥有坚定的信念很重要。
32单片机学习过程
这个就更加曲折了。。一开始看这个32的代码,简直是看天书一样,以至于出现什么一周快速学习stm32从入门到放弃再到不行还是得学之类的事情。
- 没有充分吸取51单片机的学习教训,也是了解差不多就直接下一章了。因为一个是时间紧迫,一个是因为现阶段没有学习学长学姐们口中的《微机原理》balabala…的书,就只能学会这个单片机怎么用,基本的使用原理大概是怎么个回事,实在没法特别深入地学习。
- 发现学习东西啊还是多看看视频,不要闷声看书,有人讲课的效果比自己慢慢慢慢看书的效果更好,书本可以作为对课堂细节的补充,而课堂却可以快速令人掌握一件事物的基本框架。
- 总的来说这次是一次有意义的学习体验,入门一件事情首先先了解框架(51单片机),再通过视频巩固框架(stm32的视频),书本补充细节,然后实践实践再实践,过程中的问题及时问前辈,不要闭门造车,有时候学长的一句提醒顶得上自己翻阅两天的资料。
51的程序
#include<reg52.h> sbit IN1=P1^0; sbit IN2=P1^1; sbit IN3=P1^2; sbit IN4=P1^3; sbit IN11=P1^4; sbit IN22=P1^5; sbit IN33=P1^6; sbit IN44=P1^7; void Left_moto_go() {IN1=0,IN2=1,IN11=0,IN22=1;} //小车运动的正反转电平设置 void Left_moto_back() {IN1=1,IN2=0,IN11=1,IN22=0;} void Left_moto_Stop() {IN1=0,IN2=0,IN11=0,IN22=0;} void Right_moto_go() {IN3=0,IN4=1,IN33=0,IN44=1;} void Right_moto_back() {IN3=1,IN4=0,IN33=1,IN44=0;} void Right_moto_Stop() {IN3=0,IN4=0,IN33=0,IN44=0;} unsigned char mode; //用字符来作为运行的指令 unsigned int flag; void delay(unsigned int k) { unsigned int x,y; for(x=0;x<k;x++) for(y=0;y<2000;y++); } void TIME1() // 选择使用定时器1; { TMOD=0x20; //并且工作模式2,8位 TH1=0xfd; //产生9600波特率,与蓝牙模块同步 ,因为当时购买的蓝牙模块出厂设置就是9600波特率。 TL1=0xfd; SM0=0; //设置串口的工作模式为 SM1=1; //传送8位数据 REN=1; //串口通信允许接收 TR1=1; //开定时器1 EA=1; //允许总中断 ES=1; //允许串口中断 } int main() { TIME1(); while(1) { if(flag) { ES=0; //这里一定要清零,不然下一个周期定时器中断就没法判断是否接收到信息了 if(mode=='1') //这里采用数字作为通信 { Left_moto_go() ; Right_moto_go() ; delay(20); //延时函数一定要有。 } if(mode=='3') { Left_moto_back(); Right_moto_back(); //back delay(20); } if(mode=='4') { Right_moto_go(); Left_moto_Stop(); //left delay(10); } if(mode=='5') { Left_moto_go(); Right_moto_Stop(); //right delay(10); } if(mode=='2') { Right_moto_Stop(); Left_moto_Stop(); delay(40); //stop } ES=1; flag=0; } } } void ser() interrupt 4 { RI=0; //接收中断标志 mode=SBUF; //将接收到的手机发送到缓冲区数据SBUF复制给mode flag=1; //说明已经接收数据完毕 }
这里比较简单,没有写pwm功能,将在stm32中使用pwm功能。
32单片机
这个花费了比较多的时间,一开始觉得天书,后来慢慢入门发现很多东西前人已经写好了,只要掌握使用的技巧就好了(毕竟我没有足够的专业知识,也不是搞弱电的)所以整个程序是建立在蓝牙模块的程序基础上写的。
主函数
#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "pwm.h" #include "usmart.h" #include "hc05.h" #include "usart3.h" #include "string.h" int main(void) { u16 pwmval=1; u16 speedlost=1; //转弯的时候用 u16 dang=1; u8 reclen=0; //检测获取数据 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2 delay_init(168); //初始化延时函数 uart_init(115200); //初始化串口波特率为115200 TIM2_PWM_Init(500-1,84-1); //84M/84=1Mhz的计数频率,重装载值500,所以PWM频率为 1M/500=2Khz. TIM3_PWM_Init(500-1,84-1); TIM9_PWM_Init(500-1,84-1); usmart_dev.init(84); //初始化USMART LED_Init(); //初始化LED delay_ms(1000); //等待蓝牙模块上电稳定 HC05_Init(); //初始化ATK-HC05模块 delay_ms(100); USART3_RX_STA=0; while(1) { LED0=!LED0; if(USART3_RX_STA&0X8000) //接收到一次数据了 { reclen=USART3_RX_STA&0X7FFF; //得到数据长度 USART3_RX_BUF[reclen]=0; //加入结束符 if(reclen!=0) //控制DS1检测 { if(strcmp((const char*)USART3_RX_BUF,"+SPEED1")==0) //设置挡位 //必须以+开头才可以啊! { delay_ms(1); LED1=!LED1; pwmval=1; dang=1; } if(strcmp((const char*)USART3_RX_BUF,"+SPEED2")==0) { delay_ms(1); LED1=!LED1; pwmval=100; dang=100; } if(strcmp((const char*)USART3_RX_BUF,"+SPEED3")==0) { delay_ms(1); LED1=!LED1; pwmval=200; dang=200; } if(strcmp((const char*)USART3_RX_BUF,"+FORWARD")==0) { LED1=!LED1;//测试用的,到时候可以删掉 TIM_SetCompare1(TIM9,pwmval); //右边两个轮子分别接一个PE5,PE6 TIM_SetCompare2(TIM9,pwmval); TIM_SetCompare1(TIM2,pwmval); //左边两个轮子分别接一个PA0,PA1 TIM_SetCompare2(TIM2,pwmval); delay_ms(500); } if(strcmp((const char*)USART3_RX_BUF,"+BACK")==0) { LED1=!LED1; TIM_SetCompare1(TIM3,pwmval); //右边两个轮子分别接一个PC6789 TIM_SetCompare2(TIM3,pwmval); TIM_SetCompare3(TIM3,pwmval); TIM_SetCompare4(TIM3,pwmval); delay_ms(500); } if(strcmp((const char*)USART3_RX_BUF,"+RIGHT")==0) { LED1=!LED1; speedlost=pwmval+100; TIM_SetCompare1(TIM9,speedlost); //右边两个轮子分别接一个PE5,PE6 //右边轮子变慢 TIM_SetCompare2(TIM9,speedlost); TIM_SetCompare1(TIM2,pwmval); //左边两个轮子分别接一个PA0,PA1 TIM_SetCompare2(TIM2,pwmval); delay_ms(500); } if(strcmp((const char*)USART3_RX_BUF,"+LEFT")==0) { speedlost=pwmval+100; LED1=!LED1;//测试用的,到时候可以删掉 TIM_SetCompare1(TIM9,pwmval); //右边两个轮子分别接一个PE5,PE6 TIM_SetCompare2(TIM9,pwmval); TIM_SetCompare1(TIM2,speedlost); //左边两个轮子分别接一个PA0,PA1 //左边轮子变慢 TIM_SetCompare2(TIM2,speedlost); delay_ms(500); } if(strcmp((const char*)USART3_RX_BUF,"+STOP")==0) { LED1=!LED1; //测试用的,到时候可以删掉 pwmval=500; TIM_SetCompare1(TIM9,pwmval); //右边两个轮子分别接一个PE5,PE6 TIM_SetCompare2(TIM9,pwmval); TIM_SetCompare1(TIM2,pwmval); //左边两个轮子分别接一个PA0,PA1 TIM_SetCompare2(TIM2,pwmval); TIM_SetCompare1(TIM3,pwmval); //右边两个轮子分别接一个PC6789 TIM_SetCompare2(TIM3,pwmval); TIM_SetCompare3(TIM3,pwmval); TIM_SetCompare4(TIM3,pwmval); //全部停下 delay_ms(500); pwmval=dang; } } USART3_RX_STA=0; } } }
pwm函数
#include "pwm.h" #include "led.h" #include "usart.h" //TIM4 PWM部分初始化 //PWM输出初始化 //arr:自动重装值 //psc:时钟预分频数 void TIM3_PWM_Init(u32 arr,u32 psc) { //此部分需手动修改IO口设置 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3时钟使能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能PORTC GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM4); //GPIOC6复用为定时器3 GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM4); GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_TIM4); GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_TIM4); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOC6 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC6 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //GPIOC7 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC7 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //GPIOC8 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC8 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //GPIOC9 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化PC9 TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//初始化定时器3 //初始化TIM3 Channel 6789 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低 TIM_OC1Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 4OC1 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器 TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 4OC1 TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器 TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 4OC1 TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器 TIM_OC4Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 4OC1 TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR1上的预装载寄存器 TIM_ARRPreloadConfig(TIM3,ENABLE);//ARPE使能 TIM_Cmd(TIM3, ENABLE); //使能TIM3 } void TIM2_PWM_Init(u32 arr,u32 psc) { //此部分需手动修改IO口设置 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //TIM2时钟使能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能PORTA GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM2); //GPIOA0复用为定时器2 GPIO_PinAFConfig(GPIOA,GPIO_PinSource1,GPIO_AF_TIM2); //GPIOA1复用为定时器2 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA0 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA1 TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);//初始化定时器2 //初始化TIM2 Channel 12 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低 TIM_OC1Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM2 4OC1 TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能TIM2在CCR1上的预装载寄存器 TIM_OC2Init(TIM2, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM2 4OC2 TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能TIM2在CCR2上的预装载寄存器 TIM_ARRPreloadConfig(TIM2,ENABLE);//ARPE使能 TIM_Cmd(TIM2, ENABLE); //使能TIM2 } void TIM9_PWM_Init(u32 arr,u32 psc) { //此部分需手动修改IO口设置 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9,ENABLE); //TIM9时钟使能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); //使能PORTE GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_TIM9); //GPIOE5复用为定时器9 GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_TIM9); //GPIOE6复用为定时器9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //GPIOE5 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOE,&GPIO_InitStructure); //初始化PE5/6 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIOE6 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOE,&GPIO_InitStructure); //初始化PE6 TIM_TimeBaseStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseStructure.TIM_Period=arr; //自动重装载值 TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM9,&TIM_TimeBaseStructure);//初始化定时器9 //初始化TIM9 Channel 12 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低 TIM_OC1Init(TIM9, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM9 4OC1 TIM_OC1PreloadConfig(TIM9, TIM_OCPreload_Enable); //使能TIM9在CCR1上的预装载寄存器 TIM_OC2Init(TIM9, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM9 4OC2 TIM_OC2PreloadConfig(TIM9, TIM_OCPreload_Enable); //使能TIM9在CCR2上的预装载寄存器 TIM_ARRPreloadConfig(TIM9,ENABLE);//ARPE使能 TIM_Cmd(TIM9, ENABLE); //使能TIM2 }
这里之所以调用这么多定时器,是因为另外的几个定时器被项目的其他功能占用了。
这里就只放两个主要的函数吧,蓝牙和其他函数的代码放在总的程序目录里。(懒得拷贝了。。。)硬件连接
原理图(啊别吐槽我灵魂画手了)
这里只上传51的连接
视频截图。。。
恭喜你司机同志,秋名山一较高下。
最后
再次感谢教我搭建博客,教我做车车,教我开车车的学长学姐。希望自己大学四年能开个好头,继续学习哈。(不说了昨天C语言上机跪了(手动哭泣),得赶紧复习高数把分数捞回来才行。。。)
-
蓝牙小车app
2017-02-21 19:59:16蓝牙小车app -
蓝牙小车代码
2019-01-08 15:08:08该代码能实现蓝牙小车的自动循迹和通过小车上的按键切换为蓝牙模式,且可以遇到黑线自动切换为循迹模式。 -
51单片机蓝牙小车
2019-02-25 20:41:2351单片机蓝牙小车(是我大二做的一个课程设计,小菜鸟一个,欢迎大家指正和参考。) 摘要 本次设计选择基于蓝牙遥控的多功能智能小车为对象。选用STC98C52RC单片机作为主控芯片,电机驱动采用L293N ,...51单片机蓝牙小车(是我大二做的一个课程设计,小菜鸟一个,欢迎大家指正和参考。)
摘要 本次设计选择基于蓝牙遥控的多功能智能小车为对象。选用STC98C52RC单片机作为主控芯片,电机驱动采用L293N ,电源部分采用两节3.7V锂电池供电.采用C语言模块化编程,提高开发效率.蓝牙控制功能.用按键或遥控器来控制小车.
关键词
51单片机
L298N_电机驱动
蓝牙遥控
目录
1. 前言
1.1. 系统研究背景
蓝牙属于短距离内进行无线控制和收发的通信技术,伴随着科技的飞跃性发展,也让蓝牙找到了发展的空间,它可以代替和取代落后的数字化硬件设备之间繁琐的电缆连接。在蓝牙创造的初期,没有人预料到蓝牙会有如此大的潜力和前景,而现在的发展也是完全超出了我们的预期,因为蓝牙的安全性高,制造成本低廉和所消耗的功率也是同类产品中最低的,所以被很多人使用,越来越受到了广大消费者的欢迎,基于蓝牙技术的产品也在不断的更新和投入市场。
蓝牙技术是近年来出现的新技术是一种短距离无线通信和信息传输的新型通讯科技,它使数据线的硬件设备接收更方便快捷。它可以广泛应用于世界各地,是一个蓝牙设置一个通用的范围,频率调制技术的使用,以防止外部干扰和多一些。低成本,低功耗和小辐射,和加密设置,让蓝牙的安全性更高;应用范围广,这些特点使得蓝牙技术被广泛的应用在我们日常生活中的蓝牙也支持一对一和一对多传输的通信连接,和多个蓝牙成为微网,也有网络的特点。
在现在的智能时代,小车智能控制,方便了人们的使用。在51单片机的基础下,通过蓝牙来控制小车的驾驶。
1.2. 系统研究的意义和目的因为无线技术的广泛使用,我们在研究无线和有线通信技术的方法中了解到蓝牙系统的小区域性有很大的技术突破,在国际上也得到了广泛的采纳,在市场上也有很大的需求。这也使蓝牙技术的发展成为了趋势之一,蓝牙可以发送和接受语音和数据,满足了大多数人的需求,它也融合了其他相关产品的特点,也是这样技术变得更多样性。然而,蓝牙的安全性不足,而且在小区域范围内的一点对多点的通信受到了很大的限制,这些都是其本身需要改进和完善的
可以使人们更方便,更简单的控制小车。实现了无线控制小车,摆脱了有线控制的不方便,更智能。
2. 系统概述2.1. 系统的结构
系统框图
2.2. 系统的功能
51单片机的基础下,通过蓝牙来控制驱动,此驱动能把5~12V的电压,一部分给小车轮子转动,一部分通过降压,稳压,最终降为5V来供给此驱动,单片机和传感器供电。
2.3. 开发环境
AT89C5RC单片机:
AT89C52是一种8位单片机,它是在MCS-51单片机系列上加强了一些功能后升级得到的产物。它的作用基本上就是把外界的数据和命令在中断和时钟的帮助下,在自身内部储存器上把使用者的数据进行处理。AT89C52 由8位的内部处理器,内部数据储存器(RAM,有256个字节)内部储存器有8K的大小,输入和输出双向口有32个,16位的定时器和5个两级中断,全双工串行通信口一个和时钟电路组成。
AT89C52能够自主地执行给它的指令,也就是你把所需要的指令写在单片机的内部储存器上,它会一步步执行。AT89C52可以在功耗特别低的情况下工作,就是设置成空闲选择和掉电模式两种状态来实现低功耗。
AT89C52
单片机与外界的通讯是依靠它自身的I/O 口进行的,对单片机I/O 口的控制就是对单片机的控制。I/O 口的存在不仅可以实现数据的传输,还可以改变电平和信号的性质,最重要的一点就是I/O 口可以实现与外部不同电路的连接来使单片机工作。
AT89C52单片机上我们通常使用的是4个独立的双向通用I/O 口:P0口、P1口、P2和P3口。由于外部设备的工作速率比较慢或者很快的时候,我们可以采用同步传送的方式,而单片机存在的异步传送是为了更加简练的和硬件连接进行传出。也可以通过中断传送和DMA传送来完成。AT89C52单片机还可以实现外部接口单独的编址和外部端口和存储器的统一编制两种方式。AT89C52单片机上一共有32个I/O 口,不同的I/O 口实线的作用和运行的方式都有其自己的方式,我也要通过这次课程设计,更好地了解,更熟练的运用。
3. 系统实现3.1. 相关技术技术
蓝牙技术,驱动模块,
3.2. 硬件实现
HC-06蓝牙模块
其TX接单片机的RX,RX接单片机的TX,VCC接5V(或3.3V),GND接单片机的地。HC-06蓝牙模块是通过一款手机APP(蓝牙串口助手)作为中间媒介,在蓝牙串口助手发送相关的数据到串口,串口再把相关信息送到蓝牙模块,蓝牙模块再把相关信号送到单片机中。
HC-06蓝牙模块
HC-06蓝牙模块和51单片机连接
L298N电机驱动模块,
该该电机可以驱动2路直流电机,使能端ENA,ENB,为高电频有效,
通过单片机的输入信号给IN1和IN2来控制左轮,输入信号给IN3和IN4来控制右轮。
IN1和IN2,分别给0,1 :正转
IN1和IN2,分别给1,0 :反转
IN1和IN2,分别给1,1 :停止
IN1和IN2,分别给0,0 :停止
IN3和IN4,分别给0,1 :正转
IN3和IN4,分别给1,0 :反转
IN3和IN4,分别给1,1 :停止
IN3和IN4,分别给0,0 :停止
L298N的主要引脚功能如下:
+5V:芯片电压5V;
VCC:电机电压,最大可接50V;
GND:共地接法;
Output1—Output2:输出端,接电机1;
Output3—Output4:输出端,接电机2;
EN1、EN2:高电平有效,EN1、EN2分别为 IN1和IN2、IN3和IN4的使能端;
Input1~ Input4:输入端,输入端电平和输出端电平是对应的;
L298N
3.3. 软件实现
程序流程图
51单片机,源程序:
sbit in1 = P1^0;//左电机 sbit in2 = P1^1;//左电机 sbit in3 = P1^2;//右电机 sbit in4 = P1^3;//右电机 char i; /*前进*/ void forward() { in1=1; in2=0; in3=1; in4=0; } /*后退*/ void back() { in1=0; in2=1; in3=0; in4=1; } /*左转*/ void left() { in1=1; in2=1; in3=1; in4=0; } /*右转*/ void right() { in1=1; in2=0; in3=1; in4=1; } /*停止*/ void stop() { in1=1; in2=1; in3=1; in4=1; } void main (void) { TMOD=0x20; //设置T1为工作方式2 TH1=0xfd; //装入初值,比特率为9600bps TL1=0xfd; TR1=1; //开启T1 REN=1; //接收允许 SM0=0; //方式1 SM1=1; EA=1; //开全局中断 ES=1; //开串口中断 while(1) { i=SBUF;//SBUF为单片机接收到的数据,单片机接收到的数据都存放在SBUF里; RI=0; if (i==0x02){forward();} //如果蓝牙助手发送0x02,小车前进 if (i==0x08){back();} //如果蓝牙助手发送0x08,小车后退 if (i==0x01){left();} //如果蓝牙助手发送0x01,小车左转 if (i==0x03){right();} //如果蓝牙助手发送0x03,小车右转 if (i==0x05){stop();} //如果蓝牙助手发送0x05,小车停止 } }
小车能实现基本功能,成功地用蓝牙控制小车。测试了一段时间,单片机工作稳定,蓝牙稳定的控制,小车较好的按照控制方向行走。
4. 系统使用说明1. 为小车装上两节锂电池,来供电。
2. 打开总开关,在手机端,打开一个蓝牙通讯助手(在手机应用商店可以找到下载),连接上此小车的蓝牙(HC-06),如图:
蓝牙 HC-06模块
3. 然后就可以在手机端按相应的前进,后退,左转,右转,停止等,如图:
APP
解析:前进,后退,左转,右转,停止,都对应着程序中相应的信息,如图;
当手机端发送 0x02 给蓝牙模块,就会执行 forward()函数; 小车会向前走
当手机端发送 0x08 给蓝牙模块,就会执行 back();函数;,小车会向后走
当手机端发送 0x01 给蓝牙模块,就会执行 left();函数;,小车会向左走
当手机端发送 0x03 给蓝牙模块,就会执行 right()函数;,小车会向右走
当手机端发送 0x05 给蓝牙模块,就会执行stop()函数;,小车会停止
具体的编辑如图:
APP2
最后这是小车的整体模型;
整体图
5. 总结本设计采用的是STC89C52RC单片机,这主要是因为该单片机的稳定性比较好和执行指令的速度很快。还可以采用其它系列的单片机。电机驱动采用L293N ,稳定电压,充足地向直流电机供电和稳定控制;电源部分采用两节3.7V锂电池供电,电压稳定,电流充足,还可以循环充电,节能环保。采用C语言模块化编程,提高开发效率.蓝牙控制功能.用按键或遥控器来控制小车,简单方便经过自己不断的搜索努力以及老师的耐心指导和热情帮助,本设计已经基本完成。
过这次课程设计,使我深刻地认识到学好专业知识的重要性,也理解了理论联系实际的含义,并且检验了大学两年的学习成果。虽然在这次设计中对于知识的运用和衔接还不够熟练。但是我将在以后的工作和学习中继续努力、不断完善。这两个月的设计是对过去所学知识的系统提高和扩充的过程,为今后的发展打下了良好的基础。 由于自身水平有限,设计中一定存在很多不足之处,敬请各位批评指正.
6. 参考文献C语言程序设计 :清华大学出版社作者:谭浩强
51单片机C语言教程:电子工业出版社 ,作者:郭天祥
希望对你有帮助。
为了大家方便,我上传了手机APP在网盘:https://pan.baidu.com/s/1UNjlUhUQa25K2RNW8hjwOQ
提取码:wn7k
-
stm32 蓝牙小车
2020-12-23 08:50:25蓝牙小车,很不错的学习资料 -
arduino蓝牙小车的程序
2021-01-24 15:38:35arduino蓝牙小车的程序 -
蓝牙小车代码.txt
2020-02-07 16:22:1651单片机蓝牙小车 -
蓝牙小车项目APP
2017-11-18 22:14:11这是前面蓝牙小车项目使用的的APP源码,蓝牙小车项目资源链接http://download.csdn.net/download/huangweiqing80/10124241 -
mixly蓝牙小车代码
2019-02-01 22:39:31mixly蓝牙小车代码,简单好理解,容易上手,根据自己小车的特点再进行相应的速度调整。 -
蓝牙小车iPhone客户端
2018-04-14 11:27:00蓝牙小车iPhone客户端代码,实现小车的左右,前进后退,加速功能 -
蓝牙小车源码
2013-04-01 16:33:37安卓蓝牙小车源码 -
蓝牙小车APP设计
2018-01-21 11:36:39蓝牙小车app设计的详细思路及流程,包括蓝牙模块的配置、下位机接收蓝牙数据的方法、APP设计注意事项及思路 -
蓝牙小车源代码.zip
2019-05-15 01:19:10蓝牙小车源代码.zip 内含利用蓝牙控制的简单蓝牙小车源代码 希望能够帮到大家~ -
51蓝牙小车
2014-12-18 17:53:00STC89c52单片机,keil4开发环境,文件里带有安卓蓝牙小车控制软件 -
stm32超声波蓝牙小车
2018-06-20 01:14:58stm32写的超声波壁障蓝牙小车,蓝牙用HC05 手机串口app发数控制小车转动。 -
51蓝牙小车.rar
2020-11-19 08:52:2851单片机项目设计:51单片机蓝牙小车制作教程手机控制前后左右行走) 哔哩哔哩项目展示视频:https://www.bilibili.com/video/BV1Wy4y1z7a5 -
arduino蓝牙小车教程
2016-03-08 15:32:49零基础,一步一步学会基于arduino的蓝牙小车DIY -
Arduino蓝牙小车
2019-01-23 19:47:43 -
51蓝牙小车控制程序
2016-09-26 16:29:3151蓝牙小车控制程序
-
会计初级第一章重点笔记
-
仿真钢琴-javascript实战
-
MATLAB中变量的有效索引在parfor中受限制 解决方案
-
win10系统excel2019单元格显示完整的年月日时分秒设置方法
-
微信联系人自动发送编辑的信息,vbs程序,
-
Error creating bean with name ‘enableRedisKeyspaceNotificationsInitializer‘
-
解决代理环境中“无法解析镜像源域名”问题
-
pconline1569770162324.zip
-
微信支付2021系列之付款码支付一学就会java版
-
T3方案山寨智能导航适配文件管理器
-
2020深度学习算法岗位最全面面试题
-
Spring Boot2.X仿朋友圈PC版系统实战_架构1.0
-
layui的upload模块使用
-
32
-
msvcr120.zip
-
2021面试题总结(更新ing)
-
31
-
转行做IT-第7章 数组
-
循环删除list集合中的数据
-
Java无损导出及转换word文档