基于STM32的蓝牙小车(来自LLC团队)

基于STM32的蓝牙小车(来自LLC团队)

文章目录

  • 基于STM32的蓝牙小车(来自LLC团队)
  • 引言
  • 1、系统概述
    • 1.1、设计任务
  • 2 、方案设计与论证
    • 2.1、芯片选择方案
    • 2.2、系统概述:
    • 2.3设计要求:
    • 2.4系统总体设计思路
    • 2.5、各功能模块程序实现原理分析
      • 2.5.1、主控板
      • 2.5.2 、蓝牙模块
      • 2.5.3、电机驱动模块
      • 2.5.4 、电源模块
  • 3、STM32性能介绍及硬件设计
    • 3.1 STM32单片机性能介绍
    • 3.2蓝牙小车系统硬件设计
  • 4、系统程序
    • 4.1、主程序设计如下
    • 4.2、主程序内容
      • 4.2.1、Main函数
      • 4.2.2、Pwm代码
    • 4.2.3、电机驱动代码
      • 4.2.4、蓝牙串口代码
      • 4.2.5、Android端代码
      • 4.2.6、Java代码
  • 5 、系统调试及分析
    • 5.1、系统调试
    • 5.2、调试现象及分析
    • 5.3、测试结果
  • 6、心得体会
  • 感谢LLC团队提供的资料

引言

蓝牙作为一种无线技术标准,可实现固定设备、 移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。 蓝牙自从被创制以来就得到了广泛的关注与应用, 现代许多移动设备和固定设备上都安装了蓝牙,进行文件传输、无线控制等。   蓝牙传输范围大,可穿透不同物质以及在物质间扩散;采用跳频展频技术,抗干扰性强;功耗小,成本低等等。 蓝牙的这些优势使得它在控制技术领域备受青睐。本小组的选题是蓝牙控制小车,意思就是以蓝牙作为无线控制的载体, 通过手机上的蓝牙APP对蓝牙芯片发出控制命令,通过串口传输指令给小车的核心STM32嵌入式单片机,STM32再通过对蓝牙传送的信息进行处理,控制小车各元件,LED显示屏、电机,来指导小车完成指定动作。

1、系统概述

1.1、设计任务

本设计要求实现一个手机可以远程通过蓝牙控制现车的前进、后退。左转和右转 。要求学生对嵌入式开发和安卓手机开发有一定程度的理解,熟悉嵌入式的定时器、计数器以及中断的使用,会基本的C语言和Java,熟练掌握keil5软件的使用与程序下载以及安卓手机软件开发环境。

2 、方案设计与论证

2.1、芯片选择方案

单片机处理器的选择是非常重要,如果选择的芯片型号得当,可以使整个系统的软硬件成本降低,而且性能优越,可以具有超乎想象的效果,从而使整个系统更加稳定运行。如果选择的芯片不好,那就会使整个系统的成本增加,从而极大浪费了资源,不利于资源的整合利用,有些甚至不能达到理想的效果,功能和性能不能满足要求等。单片机的选型原则是:单片机的芯片的功能要大于系统所需的功能需求;单片机选择大厂的,而不要那些小厂的;选择供应量比较大的,不选择小供应量特别小的;一定要选择比较出名的品牌,不要那些毫不知情的厂家;当然越便宜越好,可以减少成本。通过这些原则,来选择单片机模块,我们有如下两种方案:

方案一:处理器采用STM32F103芯片作为该系统的核心控制模块,该处理器是与ARM芯片的内核属于同一个版本,接口相对比较简单,处理器速度相比传统的51单片机,处理速度非常快。相比传统的51系列的单片机,该系列的单片机有许多的资源很有价值,同时该STM32F103处理器已经去除了传统的机器周期等,该处理器的处理速度也非常快,该处理器都是采用模块化设计的,界面也得到了人性化的智能处理,功能大大增多,使用起来很丰富,相比传统的51系列的单片机功能简单,该款单片机有很大的使用价值。同时在该芯片的可以连接更多的外部组件,最后达到了低成本和高性能的优点,还具有低功耗的节能优点等,按性能可以分为增强型的处理器和基本型的处理器,通过设置内置的参数可以使整个系统达到更好的性能,在该系统中,该芯片可以完全满足该设计的需要,能够实现该设计的全部功能,所以该款芯片很适合该系统。

方案二:采用AT89C51系列的单片机芯片作为核心控制模块,具有5个中断源,两个优先级,还具有两个16位的定时/计数器,只具有12T模式,内部只有128RAM的大小,工作电压只能用5V作为芯片供电电压,而且内部只有4K的存储空间,很难满足该设计的需求,该芯片缺陷太多,在市场的占有很少,主要是因为该芯片功能偏低,对于现代的科技需求还有一定的差距。在该系统中,由于该芯片功能太少,特别是内存容量比较小,所示不适合该系统。

综上所述, STM32F103ZET6 作为 MCU,该芯片是STM32F103 里面配置非常强大的了,它拥有的资源包括:64KB SRAM、 512KB FLASH、 2 个基本定时器、 4 个通用定时器、 2 个高级定时器、 2 个 DMA 控制器(共 12 个通道)、 3 个 SPI、2 个 IIC、 5 个串口、 1 个 USB、 1 个 CAN、 3 个 12 位 ADC、 1 个 12 位 DAC、 1 个 SDIO 接口、1 个 FSMC 接口以及 112 个通用 IO 口。该芯片的配置十分强悍,并且还带外部总线(FSMC)可以用来外扩 SRAM 和连接 LCD 等,通过 FSMC (可变静态存储控制器)驱动 LCD,可以显著提高 LCD 的刷屏速度,是 STM32F1 家族常用型号里面,最高配置的芯片了,所以我们选择了它作为我们的主芯片。

2.2、系统概述:

本设计是通过手机APP通过蓝牙连接HC-05模块,向HC-05发送数据,HC-05蓝牙模块通过蓝牙接收到来自APP的数据并通过UART串口传输给STM32核心板,然后STM32通过PWM来控制直流电机驱动模块,最后驱动电机。

2.3设计要求:

(1)利用stm32组成一个蓝牙小车;

(2)通过蓝牙进行数据传输,控制小车前进,后退,左转,右转。

2.4系统总体设计思路

首先搭建一个小车框架,将主板电源和电机驱动L298N还有四个轮子的马达连接起来,达到IN1和IN2控制右电机,IN3和IN4控制左电机,两个使能端,ENA控制右电机,ENB控制左电机,当IN1和IN3为高电平时实现前进,IN2和IN4为高电平时实现后退,IN1为高电平,其他为低时实现左转,IN3为高电平,其他为低时实现右转,之后再添加蓝牙模块,手机通过蓝牙串口发送ABCDE,当主板接受到后根据不同的数据进行前进后退等等

2.5、各功能模块程序实现原理分析

该系统分为主控板,蓝牙模块,电机驱动模块,电源管理模块,Android控制端等模块。

2.5.1、主控板

stm32是一个低功耗,高性能32位单片机,片内含4k Bytes ISP(In-system programmable)的可反复擦写1000次的Flash只读程序存储器。主要性能有:与MCS-51单片机产品兼容、全静态操作:0Hz~33Hz、 三级加密程序存储器、32个可编程I/O口线、三个16位定时器/计数器、八个中断源、全双工UART串行通道、掉电后中断可唤醒、看门狗定时器、双数据指针、掉电标识符、易编程。
基于STM32的蓝牙小车(来自LLC团队)_第1张图片
图1.STM32F103引脚图

2.5.2 、蓝牙模块

使用HC-05蓝牙模块就接受APP的蓝牙数据。挂载在STM32板UART中。HC-05 蓝牙串口通信模块,是基于 Bluetooth Specification V2.0 带 EDR 蓝牙协议的 数传模块。无线工作频段为 2.4GHz ISM,调制方式是 GFSK。模块最大发射功率为 4dBm, 接收灵敏度-85dBm,板载 PCB 天线,可以实现 10 米距离通信。 模块采用邮票孔封装方式,模块大小 27mm×13mm×2mm,方便客户嵌入应用系统之 内,自带 LED 灯,可直观判断蓝牙的连接状态。 模块采用 CSR 的 BC417 芯片,支持 AT 指令,用户可根据需要更改角色(主、从模式) 以及串口波特率、设备名称等参数,使用灵活。实物图如下:
基于STM32的蓝牙小车(来自LLC团队)_第2张图片
图2.蓝牙模块实物图

基于STM32的蓝牙小车(来自LLC团队)_第3张图片
图3.工作原理图
基于STM32的蓝牙小车(来自LLC团队)_第4张图片
图4.蓝牙模块与手机通讯

2.5.3、电机驱动模块

使用直流电机驱动模块L298N驱动电机,挂载在STM32板GPIO复用PWM口。L298N的封装。H桥电路虽然有着诸多的优点,但是在实际制作过程中,由于元件较多,电路的搭建也较为麻烦,增加了硬件设计的复杂度。由于H桥电路有诸多的优点,但是在实际制作过程中电路又比较麻烦,因此在本设计中我们采用H桥集成电机驱动芯片L298。L298N的工作原理和以上介绍的H桥相同,引脚图如图下所示:
基于STM32的蓝牙小车(来自LLC团队)_第5张图片
图5.L298N封装图

L298N的原理图设计
L298N是ST公司生产的一种高电压、大电流电机驱动芯片。该芯片采用15脚封装。主要特点是:工作电压高,最高工作电压可达46V;输出电流大,瞬间峰值电流可达3A,持续工作电流为2A;额定功率25W。内含两个H桥的高电压大电流全桥式驱动器,可以用来驱动直流电动机和步进电动机、继电器线圈等感性负载;采用标准逻辑电平信号控制;具有两个使能控制端,在不受输入信号影响的情况下允许或禁止器件工作有-一个逻辑电源输入端,使内部逻辑电路部分在低电压下工作;可以外接检测电阻,将变化量反馈给控制电路。使用L298N芯片驱动电机,该芯片可以驱动一台两相步进电机或四相步进电机,也可以驱动两台直流电机。L298的参考电路图如图下所示:

1、控制板内部带5V逻辑电平转换芯片,不需要额外的5V供电。

2、如果ENA、ENB不接PWM调速信号,则无法控制速度。

3、GND和12V接电机电源,其中12V 可以接7V–24V电平,板子上的电源开关只是控制逻辑5V电平的开关,L298 的12V电源不受此开关控制.

4、输出端和输入端一-对应, 当输入端为5V时,输出端也为高电平,输入端为低电平时输出亦为低电平。
基于STM32的蓝牙小车(来自LLC团队)_第6张图片
图6.L298N逻辑图
基于STM32的蓝牙小车(来自LLC团队)_第7张图片
图7.电机驱动模块实物

2.5.4 、电源模块

使用可充电电池组为STM32板和之直流电机驱动模块供电。电源是整个系统稳定工作的前提,因此必须有-个合理的电源设计,对于小车来说电源设计应注意两点:

(1)与一般的稳压电源不同,小车的电池电压一 般在6-8V左右,还要考虑在电池损耗的情况下电压的降低,因此常用的78系列稳压芯片不再能够满足要求,因此必须采用低压差的稳压芯片,在本文中以较为常见的LM2940-5. 0为例。

(2)单片机必须与大电流器件分开供电,避免大电流器件对单片机造成干扰,影响单片机的稳定运行。
基于STM32的蓝牙小车(来自LLC团队)_第8张图片
图8.电源实物图

3、STM32性能介绍及硬件设计

3.1 STM32单片机性能介绍

STM32它拥有的资源包括:48KB SRAM、256KB FLASH、2 个基本定时器、4 个通用定时器、2个高级定时器、2个DMA 控制器(共 12 个通道)、3 个SPI、2个IIC、5个串口、1个USB、1个CAN、3个12位ADC、1个12位DAC、1个SDIO接口及51 个通用IO口,该芯片性价比极高。

各个引脚说明如下

PB6 接电机驱动的IN1;

PB7接电机驱动IN2;

PB8接电机驱动IN3;

PB9接电机驱动IN4;

PA2接蓝牙模块的RXT;

PA3接蓝牙模块的TXD;

PA6接电机驱动ENA,作为右电机调速;

PA7接电机驱动ENB,作为左电机调速;

3.2蓝牙小车系统硬件设计

基于STM32的蓝牙小车(来自LLC团队)_第9张图片
图9.蓝牙模块电路图
基于STM32的蓝牙小车(来自LLC团队)_第10张图片
图10.电源电路图

4、系统程序

4.1、主程序设计如下

主程序流程设计图如下图:

基于STM32的蓝牙小车(来自LLC团队)_第11张图片
图11.主程序流程设计图

4.2、主程序内容

4.2.1、Main函数

int main(void)
{
     			

	delay_init();	  	 //延时函数初始化	 

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

	uart_init(9600);	 //串口初始化为115200

 LED_Init();			   //LED端口初始化

 TIM3_PWM_Init(899,719);	 //72分频。PWM频率=72000000/(900*720)

	Moter_GPIO_Init();//电机的GPIO初始化

	OLED_Init();  // OLED初始化

//	LineWalking_GPIO_Init();// 巡线传感器GPIO初始化接口

//	FAN_Init();       //风扇GPIO初始化接口 

 

	OLED_Clear();  //清屏函数,清完屏,

	OLED_ShowString(10,10,"hello");

	 LED = 1;

  while(1)

	{
     					 

//		TIM_SetCompare1(TIM3,PWM);	//	电机1占空比  

//		TIM_SetCompare2(TIM3,PWM);	//	电机2占空比
			app_LineWalking();
		//LED = 0;
		delay_ms(1000);
		}	 

 }

4.2.2、Pwm代码

void TIM3_PWM_Init(u16 arr,u16 psc)
{
       
	GPIO_InitTypeDef GPIO_InitStructure;

	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟

	

	//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5   

 

  //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形	GPIOA.6 GPIOA.7

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6; //TIM_CH1 TIM_CH2

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出

	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO

 

  //初始化TIM3

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值

	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 

	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim

	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

	//初始化TIM3 Channel2 PWM模式   

 

 //------------------注意!!!----------------------//

 //在模式PWM1中有效电平于无效电平  such as (TIM_OCPolarity_Low)有效 则为低电平  否则无效电平为高电平

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性低

	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2

	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器

	//初始化TIM3 Channel1 PWM模式   

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能

	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性低

	TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC1

 	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器

	TIM_Cmd(TIM3, ENABLE);  //使能TIM3

}

4.2.3、电机驱动代码

void Moter_GPIO_Init(void)
{
     
	//电机的GPIO 初始化

	GPIO_InitTypeDef  GPIO_InitStructure;
 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 //使能PB端口时钟
		
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_9;				 //端口配置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出

	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz

	GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化

	GPIO_SetBits(GPIOB,GPIO_Pin_4); 						 // 输出高 

	GPIO_SetBits(GPIOB,GPIO_Pin_5); 						 // 输出高

	GPIO_SetBits(GPIOB,GPIO_Pin_8); 						 // 输出高

	GPIO_SetBits(GPIOB,GPIO_Pin_9); 						 // 输出高

}
 
//前进

void moter_run(u16 pwm1,u16 pwm2)
{
     
	IN1 = 1; IN2 = 0;

	IN3 = 1; IN4 = 0;

	TIM_SetCompare1(TIM3,pwm1);	//	电机1占空比  

	TIM_SetCompare2(TIM3,pwm2);	//	电机2占空
}

//暂停

void moter_stop(void)
{
     
	IN1 = 0; IN2 = 0;

	IN3 = 0; IN4 = 0;

	TIM_SetCompare1(TIM3,0);	//	电机1占空比  

	TIM_SetCompare2(TIM3,0);	//	电机2占空比 
}

 

//后退

void moter_back(u16 pwm1, u16 pwm2)
{
     
	IN1 = 0; IN2 = 1;

	IN3 = 0; IN4 = 1;

	TIM_SetCompare1(TIM3,pwm1);	//	电机1占空比  

	TIM_SetCompare2(TIM3,pwm2);	//	电机2占空比 

}

// Function    moter_left

// @author     yuan

//left

void moter_left(u16 pwm1, u16 pwm2)
{
     
	IN1 = 0; IN2 = 0;

	IN3 = 1; IN4 = 0;

	TIM_SetCompare1(TIM3,pwm1);	//	电机1占空比  

	TIM_SetCompare2(TIM3,pwm2);	//	电机2占空比 

}

 

//Right

void moter_right(u16 pwm1,u16 pwm2)
{
     
	IN1 = 1; IN2 = 0;
	IN3 = 0; IN4 = 0;
	TIM_SetCompare1(TIM3,pwm1);	//	电机1占空比  
	TIM_SetCompare2(TIM3,pwm2);	//	电机2占空比 

}

4.2.4、蓝牙串口代码

void uart_init(u32 bound)
{
     
    //GPIO端口设置

    GPIO_InitTypeDef GPIO_InitStructure;

    USART_InitTypeDef USART_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟

    //USART1_TX  GPIOA.9

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

    //USART1_RX	 GPIOA.10初始化

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入

    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

    //Usart1 NVIC 配置

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能

    NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器

    //USART 初始化设置
    USART_InitStructure.USART_BaudRate = bound;//串口波特率

    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式

    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位

    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位

    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制

    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
    USART_Init(USART1, &USART_InitStructure); //初始化串口1
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
    USART_Cmd(USART1, ENABLE);           //使能串口1 

}

void USART1_IRQHandler(void)         //串口1中断服务程序
{
     

	u8 Res;

#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.

	OSIntEnter();   

#endif

	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)

		{
     

		Res =USART_ReceiveData(USART1);	//读取接收到的数据

//			

			if(Res == 69) //E

				moter_stop();

			

			if(Res == 65) //A

			{
     

				moter_run(300,300);

			LED = !LED;

			}

			if(Res == 66) //B

				moter_left(300,0);	

			if(Res == 67) //C

				moter_right(0,300);

			if(Res == 68) //D

				moter_back(300,300);

			

//				OLED_Clear();  //清屏函数,清完屏,

//				OLED_ShowString(10,12,"ok");

		

		if((USART_RX_STA&0x8000)==0)//接收未完成

			{
     

//				OLED_ShowString(10,10,"ok");

			if(USART_RX_STA&0x4000)//接收到了0x0d

				{
     

//					OLED_ShowString(10,10,"ok");

				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始

				else USART_RX_STA|=0x8000;	//接收完成了 

				}

			else //还没收到0X0D

				{
     	

				if(Res==0x0d)USART_RX_STA|=0x4000;

				else

					{
     

					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;

					USART_RX_STA++;

					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	 

					}		 

				}

			}  		 

   } 

#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.

	OSIntExit();  										 

#endif

}

4.2.5、Android端代码

Layout

<Button

     android:layout_width="100dp"

     android:layout_height="wrap_content"

     android:id="@+id/forword"

      android:layout_gravity="center_horizontal"

      android:text="前进"

      android:textSize="20dp"

      android:layout_margin="20dp"

      android:background="@drawable/button"

      />

    <LinearLayout

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:orientation="horizontal"

      android:layout_gravity="center"

     \>

      <Button

        android:id="@+id/left_btn"

        android:layout_width="100dp"

        android:layout_height="wrap_content"

        android:text="左转"

        android:textSize="20dp"

        android:background="@drawable/button"

        />

      <Button

        android:id="@+id/stop_btn"

        android:layout_width="100dp"

       android:layout_height="wrap_content"

        android:text="停止"

       android:textSize="20dp"
        android:layout_marginLeft="20dp"
        android:background="@drawable/button"
        />
      <Button

       android:id="@+id/right_btn"

        android:layout_width="100dp"

        android:layout_height="wrap_content"

        android:text="右转"

        android:textSize="20dp"

        android:layout_marginLeft="20dp"

        android:background="@drawable/button"

        />

    LinearLayout>

    <Button

     android:id="@+id/back"

     android:layout_width="100dp"

     android:layout_height="wrap_content"

      android:layout_gravity="center"

       android:text="后退"

      android:textSize="20dp"

      android:background="@drawable/button"

      android:layout_margin="20dp"

      />

4.2.6、Java代码

class ButtonListener implements View.OnTouchListener {
     
    
    public boolean onTouch(View v, MotionEvent event) {
     

      switch (v.getId()){
     

        case R.id.forword:

          if(event.getAction() == MotionEvent.ACTION_UP){
     //放开事件

            message[0]= (byte) 0x45;//设置要发送的数值

           bluesend(message);//发送数值

            Log.d("cy08",""+message[0]);

         }

          if(event.getAction() == MotionEvent.ACTION_DOWN){
     //按下事件

            message[0]= (byte) 0x41;//设置要发送的数值

            bluesend(message);//发送数值

            Log.d("cy08",""+message[0]);
          }

          break;

        case R.id.back:

          if(event.getAction() == MotionEvent.ACTION_UP){
     //放开事件

            message[0]= (byte) 0x45;//设置要发送的数值

           bluesend(message);//发送数值

            Log.d("cy08",""+message[0]);
          }

          if(event.getAction() == MotionEvent.ACTION_DOWN){
     //按下事件

            message[0]= (byte) 0x44;//设置要发送的数值

            bluesend(message);//发送数值

            Log.d("cy08",""+message[0]);

          }

          break;

        case R.id.stop_btn:

          if(event.getAction() == MotionEvent.ACTION_UP){
     //放开事件

            message[0]= (byte) 0x45;//设置要发送的数值

            bluesend(message);//发送数值

            Log.d("cy08",""+message[0]);

          }

          if(event.getAction() == MotionEvent.ACTION_DOWN){
     //按下事件

            message[0]= (byte) 0x45;//设置要发送的数值

            bluesend(message);//发送数值

            Log.d("cy08",""+message[0]);

          }

          break;

        case R.id.left_btn:

          if(event.getAction() == MotionEvent.ACTION_UP){
     //放开事件

            message[0]= (byte) 0x45;//设置要发送的数值

            bluesend(message);//发送数值

           Log.d("cy08",""+message[0]);

          }

          if(event.getAction() == MotionEvent.ACTION_DOWN){
     //按下事件

            message[0]= (byte) 0x42;//设置要发送的数值

            bluesend(message);//发送数值

            Log.d("cy08",""+message[0]);

          }

          break;

       case R.id.right_btn:

          if(event.getAction() == MotionEvent.ACTION_UP){
     //放开事件

            message[0]= (byte) 0x45;//设置要发送的数值

           bluesend(message);//发送数值

            Log.d("cy08",""+message[0]);

          }

          if(event.getAction() == MotionEvent.ACTION_DOWN){
     //按下事件

            message[0]= (byte) 0x43;//设置要发送的数值

            bluesend(message);//发送数值

           Log.d("cy08",""+message[0]);

          }

          break;

        default:

          break;

      }

      return false;

    }

  }

5 、系统调试及分析

5.1、系统调试

根据系统设计方案,本系统的调试共分为三大部分:硬件调试,软件调试和软硬件联调。本系统设计中采用模块设计法,所以方便对各个电路模块进行调试:将主控板,蓝牙模块,电机驱动模块,电源管理模块,Android控制端等进行调试,最后将软件和硬件放在一起进行调试。

硬件调试:蓝牙模块

基于STM32的蓝牙小车(来自LLC团队)_第12张图片
图13.数据接收发送
基于STM32的蓝牙小车(来自LLC团队)_第13张图片

图14.蓝牙数据接收原理图

软件调试:
基于STM32的蓝牙小车(来自LLC团队)_第14张图片

图15.软件调试截图

5.2、调试现象及分析

进入调试状态后应该在不同功能的地方设置断点然后按步运行,通过手机AAP进行控制,同时观察小车是否按照我们设计的要求来做出反应,当小车按照我们的设计进行反应时,我们将按照此方法依次对其他功能进行测试,并观察小车的运动,当小车没有按照我们原设定的操作进行,我们先观察代码是否有问题,通过控制变量来,排除小车出现的问题。按照上述操作,我们对小车进行一步一步的调试时。在调试的过程中我们也遇到了不同的问题,第一个问题,我们通过手机发送蓝牙的数据,但是硬件那边没有做出相应的动作,在我们查阅资料发现是软件发送给小车,小车得到数据硬件识别不了,在更改数据之后,我们以为小车可以做出原设定的动作,在发送数据过去小车没有做出动作。这就引出第二个问题。第二个问题,是串口的问题,我们检查代码和小车各模块的链接之后,初步断定是串口的问题,换了串口之后,问题并没有解决,在和队友商量之后,我们更换了核心板,发现某一些管脚的输出有一些问题,在更改管脚后,最后完成了对小车控制。对程序的调试能力还需要加强,程序的调试需要耐心,而且需要熟练掌握软件的跟部分功能。

5.3、测试结果

基于STM32的蓝牙小车(来自LLC团队)_第15张图片

6、心得体会

本次的嵌入式系统原理及应用课程实践是一次非常珍贵的提升个人自身能力的经历。该课程实践非常考究学生的编程能力以及训练学生对项目全局把握的一种训练。我们小组做的是蓝牙小车,就是手机通过蓝牙控制小车前进左右。个人觉得该项目还是有很大的现实意义的,因为现在的无人机非常热门,虽然蓝牙连接相对来说还是有点弱,对于我们这些初学者还算是比较友好的。在做这个项目的过程中,我们遇到了许多问题,首先,我们把小车组装好,把蓝牙模块HC-05配置好,然后连接上STM32芯片上,然后在电脑上用串口助手调试,显示发送数据是正常的,然后驱动小车时,小车不动,经过组员们的讨论以及请教同学,发现了芯片的串口管脚有些问题,经过与组员们讨论,更换主核心板子,发现也是不行,更换杜邦线也是不行,最后更改连接驱动模块的管脚,手机APP通过蓝牙成功控制小车前进、后退、右转和左转这些操作。我们不知道是板子的问题还是串口的问题,但还是完成的课程实践,虽然还是有点小小的遗憾。通过本次的课程实践,让我知道了自己的短板,同时也提升了我的编程能力以及独立思考的能力,受益无穷。

感谢LLC团队提供的资料

你可能感兴趣的:(stm32,stm32,蓝牙小车,经验分享)