Stm32单片机通过蓝牙串口与手机端通信简单可扩展,有代码亲测可用

HC-05蓝牙模块--------手机与STM32通信(代码编写)(上位机配置)保姆级教程_stm32蓝牙模块代码-CSDN博客

看完上一篇博客,不用做实验,做了更好,没做出来也不要担心,有个大致了解就行

下面是蓝牙手机调试器

链接:https://pan.baidu.com/s/1Z3wT3xi3yf2EM8InjYzRZQ?pwd=45md 
提取码:45md

实现此功能我弄了四个文件

bsp_usart.c    蓝牙串口源文件

#include "bsp_usart.h"
#include "math.h"
#include "sys.h"
#include <stdarg.h>
#include "function.h"
/*******************************************************
len的长度代表接收数据包的字节13=1字节(包头)+2字节(按键)+8字节(2个int)+2(校验位和包尾);
根据要求设置相应数据,然后在调试器上设置数据包
*******************************************************/

uint8_t USART_TX_BUF[USART_TX_LEN];	//数据包缓存区

__IO uint8_t usart_value=0;//接收一个字节数据的变量

uint8_t len=0;   //接收数据的数组当前下标   
uint8_t Flag=0;  //接收到数据之后Flag=1
static uint8_t f = 0; //从0xA5开始接收0x5A结束
// 中断服务函数
 void  DEBUG_USART_IRQHandler   (void){
		//printf("1\r\n");
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)){//接收中断标志位变化
		usart_value=USART_ReceiveData(DEBUG_USARTx);    //接收一个字节的数据
		if(usart_value == 0xA5)                         //从0xA5开始
		{
			USART_TX_BUF[len]=usart_value;
			len++;
			f = 1;
				
		}
	else	if(len==REC_BUF_SIZE || usart_value == 0x5A){       //接收到包尾,结束本次接收
		USART_TX_BUF[len]=usart_value;
		len++;
		Flag=1;
		len=0;
		f = 0;
				//printf("3\r\n");
	}
	else	if(f == 1)                                      //0xA5之后的数据存放到num[]数组
		{
			USART_TX_BUF[len]=usart_value;
			len++;
					//printf("%d\r\n",usart_value);
		}
		
	}
	
	else if(len > REC_BUF_SIZE){                        //如果长度大于数据包的长度,也结束本次接收
		f = 0;
		len = 0;
	}
 	USART_ClearFlag(DEBUG_USARTx,USART_IT_RXNE);        //清除中断标志位
}

 void NVIC_Config(void){
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitStructure.NVIC_IRQChannel=DEBUG_USART_IRQ ;
	
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	
	NVIC_Init(&NVIC_InitStructure);


}
//
void USART_Config(void){
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
	RCC_APB2PeriphClockCmd(DEBUG_USART_GPIO_CLK ,ENABLE);
	
	RCC_APB1PeriphClockCmd( DEBUG_USART_CLK,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin= DEBUG_USART_TX_GPIO_PIN ;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin=DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStructure);

	USART_InitStructure.USART_BaudRate=DEBUG_USART_BAUDRATE;
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;
	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(DEBUG_USARTx,&USART_InitStructure);
	
	NVIC_Config();
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(DEBUG_USARTx, ENABLE);                    //使能串口
}
//
void Usart_SendByte(USART_TypeDef*pUSARTx,uint8_t data){
	USART_SendData(pUSARTx,data);
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET);
}

//void USART4_printf(char *fmt,...)
//{
// char buffer[usart_txBuff];
//	uint16_t i=0;
// va_list arg_ptr;
//	va_start(arg_ptr,fmt);
//	vsnprintf(buffer,usart_txBuff,fmt,arg_ptr);
//	while(i<99&&buffer[i])
//	{
//	  Usart_SendByte(UART4,buffer[i]);
//	   i++;
//	}
//	va_end(arg_ptr);
//}
//void HC_05_USART4_printf(char *fmt,...)
//{
//	 char buffer[usart_txBuff];
//	 va_list arg_ptr;
//	u8 sum=0;//校验位--数据字节之和的低八位
//		uint16_t i=0;
//	Usart_SendByte(UART4,0xA5);//头7
//	///发送模式

//	va_start(arg_ptr,fmt);
//	//把你参数的全部数据转换成字符一位一位写进去,%f 78.9->37 38 2e 39 30 30 30 30 30 
//	vsnprintf(buffer,usart_txBuff,fmt,arg_ptr);
//	while(i<99&&buffer[i])
//	{
//	
//	  Usart_SendByte(UART4,buffer[i]);
//		sum+=buffer[i];
//	   i++;
//		//	printf("%x\r\n",buffer[i]);
//	}
//	  Usart_SendByte(UART4,sum);//校验位
//		Usart_SendByte(UART4,0x5A);//尾
//	va_end(arg_ptr);
//}

bsp_usart.h    蓝牙串口头文件

#ifndef __BSP_USART_H
#define __BSP_USART_H
#include "stm32f10x.h"
#include <stdio.h>

#define REC_BUF_SIZE 30    //接收数据包的最大值

#define DEBUG_USARTx                     USART3   //蓝牙所用串口3
#define DEBUG_USART_CLK                  RCC_APB1Periph_USART3  //串口时钟
#define DEBUG_USART_APBxClkCmd           RCC_APB1PeriphClockCmd //串口时钟使能
#define DEBUG_USART_BAUDRATE             9600 //波特率设置·

#define DEBUG_USART_GPIO_CLK             RCC_APB2Periph_GPIOB   

#define DEBUG_USART_GPIO_APBxClkCmd       RCC_APB2PeriphClockCmd  //端口时钟

#define DEBUG_USART_TX_GPIO_PORT         GPIOB              //端口宏定义
#define DEBUG_USART_TX_GPIO_PIN          GPIO_Pin_10
#define DEBUG_USART_RX_GPIO_PORT         GPIOB
#define DEBUG_USART_RX_GPIO_PIN          GPIO_Pin_11

#define DEBUG_USART_IRQ                  USART3_IRQn
#define DEBUG_USART_IRQHandler           USART3_IRQHandler   //中断服务函数


#define usart_txBuff  100
#define USART_TX_LEN 60	//数据包大小

void NVIC_Config(void);
void USART_Config(void);
void Usart_SendByte(USART_TypeDef*pUSARTx,uint8_t data);
//void USART4_printf(char *fmt,...);
//void HC_05_USART4_printf(char *fmt,...);
#endif 

        关键是下面的两个文件,我看了http://t.csdn.cn/4z2qm这篇博客后,写代码时你会发现,当你用蓝牙接收发送不同的数据时,你需要对你的各种类型的数据进行字节转换,每次还需要手动更改校验位,还需要精确到数据的字节和,一个不对就会通信失败,

所以我用了void Int_to_Byte(int i,uint8_t *byte)等函数进行字节转换,

  用BL_Send(USART_TypeDef*pUSARTx,int adcl,int adcm,int adcr,int encoderl,int encoder,int lpwm,int rpwm,int lpwm_bc,int rpwm_bc)里面进行数据处理整合,用串口一字节一字节发送,步骤比下一种方法麻烦太多,还极容易出错。

其实此代码里的最后部分,我利用共用体实现的两个函数就实现了所有的功能(可怜我现在才发现),而且非常容易扩展了。

#include "function.h"
#include "bsp_usart.h"
#include "math.h"
#include "stm32f10x_it.h" 
#include "stdlib.h"
#include "bsp_usart.h"


extern uint8_t Flag;//数据包是否发送
extern uint8_t USART_TX_BUF[USART_TX_LEN];	//数据包缓存区

void Int_to_Byte(int i,uint8_t *byte)
{

	unsigned long longdata = 0;
	longdata = *(unsigned long*)&i;          
	byte[3] = (longdata & 0xFF000000) >> 24;
	byte[2] = (longdata & 0x00FF0000) >> 16;
	byte[1] = (longdata & 0x0000FF00) >> 8;
	byte[0] = (longdata & 0x000000FF);

}
void Float_to_Byte(float f,uint8_t *byte)
{

	unsigned long longdata = 0;
	longdata = *(unsigned long*)&f;          
	byte[3] = (longdata & 0xFF000000) >> 24;
	byte[2] = (longdata & 0x00FF0000) >> 16;
	byte[1] = (longdata & 0x0000FF00) >> 8;
	byte[0] = (longdata & 0x000000FF);

}

void Short_to_Byte(short s,uint8_t *byte)
{
      
	byte[1] = (s & 0xFF00) >> 8;
	byte[0] = (s & 0xFF);
}

/**************************************************************************
函数名:BL_Send
发送给蓝牙
作用:  上位机数据显示,板子发送上位机,根据要发送的数据字节,在调试器上设置接收数据包
				参数1代表串口,后面代表发送的数据,可根据实际情况进行更改

参数:(串口类型,要发送的参数1,参数2,参数3)可修改个数,同时也要修改发送的字节就是下面注释掉的部分

使用:BL_Send(DEBUG_USARTx,mode,quan)
***************************************************************************/

void BL_Send(USART_TypeDef*pUSARTx,int adcl,int adcm,int adcr,int encoderl,int encoder,int lpwm,int rpwm,int lpwm_bc,int rpwm_bc)
{
	
	u8 i;
	Usart_SendByte(pUSARTx,0xA5);//头
	
	///发送模式
	Int_to_Byte(adcl,&USART_TX_BUF[1]);
	Int_to_Byte(adcm,&USART_TX_BUF[5]);
	Int_to_Byte(adcr,&USART_TX_BUF[9]);
	Int_to_Byte(encoderl,&USART_TX_BUF[13]);
	Int_to_Byte(encoder,&USART_TX_BUF[17]);
	Int_to_Byte(lpwm,&USART_TX_BUF[21]);
	Int_to_Byte(rpwm,&USART_TX_BUF[25]);
	Int_to_Byte(lpwm_bc,&USART_TX_BUF[29]);
	Int_to_Byte(rpwm_bc,&USART_TX_BUF[33]);
	
	
	USART_TX_BUF[37] =(uint8_t) ((USART_TX_BUF[1]+USART_TX_BUF[2]+USART_TX_BUF[3]+USART_TX_BUF[4]+USART_TX_BUF[5]+USART_TX_BUF[6]+USART_TX_BUF[7]+USART_TX_BUF[8]+USART_TX_BUF[9]+
	USART_TX_BUF[10]+USART_TX_BUF[11]+USART_TX_BUF[12]+USART_TX_BUF[13]+USART_TX_BUF[14]+USART_TX_BUF[15]+USART_TX_BUF[16]+USART_TX_BUF[17]+USART_TX_BUF[18]+USART_TX_BUF[19]+USART_TX_BUF[20]+USART_TX_BUF[21]+USART_TX_BUF[22]+USART_TX_BUF[23]
	+USART_TX_BUF[24]+USART_TX_BUF[25]+USART_TX_BUF[26]+USART_TX_BUF[27]+USART_TX_BUF[28]+USART_TX_BUF[29]+USART_TX_BUF[30]+USART_TX_BUF[31]+USART_TX_BUF[32]+USART_TX_BUF[33]+USART_TX_BUF[34]
	+USART_TX_BUF[35]+USART_TX_BUF[36]));

	for(i=1;i<=36;i++)
	{
		Usart_SendByte(pUSARTx,USART_TX_BUF[i]);
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TC) == RESET) {}	
	}
	
	Usart_SendByte(pUSARTx,USART_TX_BUF[37]);//校验位
	Usart_SendByte(pUSARTx,0x5A);//尾
	
}
union InputData Receive_Data_Handle(void){
	    union InputData  a;
			int  i=0;
			for(i=0;i<sizeof(DataRev);i++)
	   { 

		    a.bite[i]=USART_TX_BUF[i];
	   }
			Flag=0;    //接收完成
			return a;//操作的是a->data.baotou;
}

void BLuetooth_Send(union OnputData *send_data,USART_TypeDef*pUSARTx)
{
	u8 i=0,jyw=0;
		Usart_SendByte(pUSARTx,send_data->bite[0]);
	for(i=1;i<sizeof(DataOut)-2;i++)
	{
		Usart_SendByte(pUSARTx,send_data->bite[i]);
		jyw+=send_data->bite[i];
	}
	Usart_SendByte(pUSARTx,jyw);//尾
	Usart_SendByte(pUSARTx,send_data->bite[sizeof(DataOut)-1]);//尾
}






#ifndef __FUNCTION_H
#define __FUNCTION_H
	
#include "stm32f10x.h"
#include "bsp_usart.h"

#pragma pack(1)
 typedef struct DataRev {
  u8 baotou;
	int data1;
	u8  jyw;
	u8   baowei;
	
}DataRev;
#pragma pack()
 union InputData{
  DataRev data;
  uint8_t bite[sizeof(DataRev)];
};
 #pragma pack(1)
 typedef struct DataOut {
  u8 baotou;
	int data1;
	float  data2;
	float data3;
	u8  jyw;
	u8   baowei;
	
}DataOut;
#pragma pack()
union OnputData{
  DataOut data;
  uint8_t bite[sizeof(DataOut)];
};

union InputData Receive_Data_Handle(void);//接收上位机数据
void BL_Send(USART_TypeDef*pUSARTx,int adcl,int adcm,int adcr,int encoderl,int encoder,int lpwm,int rpwm,int lpwm_bc,int rpwm_bc);
void BLuetooth_Send(union OnputData *send_data,USART_TypeDef*pUSARTx);
#endif 

用共用体就可以实现对一片内存空间不同的访问方式了。这一点在串口通信方面实在有用。

接下来实操(以增加发送一个float数据为例),操作时你只需要在DataOut类型里面增加一个类型,然后再mian里面给他赋值就行了,注意相同类型的数据要按顺序,因为蓝牙调试器里面相同类型的数据是放在一起的。所以我在下面加了一个float。

typedef struct DataOut {
  u8 baotou;
	int data1;
	float  data2;
	float data3;
	float data4;
	u8  jyw;
	u8   baowei;
	
}DataOut;

 
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "lcd.h"
#include "usart.h"	 
#include "adc.h"
#include  "motor.h"
#include  "interrupt.h"
#include "PID.h"
#include "encorder.h"
#include "math.h"
#include  "control.h"
#include "bsp_usart.h"
#include "function.h"

int adcl,adcm,adcr;
extern uint8_t Flag;//数据包是否发送;
int main(void)
 {
	union InputData dis;
	union OnputData send_d;
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 	//串口初始化为115200
	USART_Config();
	 
	send_d.data.baotou=0XA5;
  send_d.data.data1=999;
	send_d.data.data2=4.5464;
	send_d.data.data3=5.5464;
	send_d.data.data4=1.2345;//增加的
	send_d.data.baowei=0X5A;
 
	while(1)
	{
		if(Flag==1)
	  {
		
			dis=Receive_Data_Handle();
			printf("%d\r\n", dis.data.data1);
			 BLuetooth_Send(&send_d,USART3);
		}
	}
 }

我这里是蓝牙给单片机发一个数据之后,单片机再给蓝牙发数据

手机端设置

手机端进入专业调试,新建建一个工程后,点击通信设置按照DataOut类型设置格式

 然后编辑控件,点击加号新建一个可编辑文本用于给32单片机发数据,新建四个文本分别链接到数据包里面定义的的四个数据。

注意代码开始开始之后,先手机发送INT0数据,a,b,c,的值才会改变。

 

 可以看到数据传输正确,这种方法完全不需要考虑上面所提到的问题,而且数据包改变对代码的改动非常少,扩展性比之前的高。

期间的问题总结:

结构体禁用对齐

空指针导致程序卡住问题

void BLuetooth_Send(union OnputData *send_data,USART_TypeDef*pUSARTx)
{
    u8 i=0,jyw=0;
        Usart_SendByte(pUSARTx,send_data->bite[0]);
    for(i=1;i<sizeof(DataOut)-2;i++)//sizeof(DataOut)-2的问题
    {
        Usart_SendByte(pUSARTx,send_data->bite[i]);
        jyw+=send_data->bite[i];
    }
    Usart_SendByte(pUSARTx,jyw);//尾
    Usart_SendByte(pUSARTx,send_data->bite[sizeof(DataOut)-1]);//sizeof(DataOut)-1的问题
}

  • 10
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
HC-05是一款常用的串口蓝牙模块,可以与STM32微控制器实现无线通信。下面简单介绍一下如何通过HC-05与手机APP进行通信。 首先,我们需要在手机上下载并安装一个支持蓝牙通信的APP。常用的APP有Bluetooth Terminal、Serial Bluetooth等。这些APP具有数据接收和发送的功能。 接下来,我们需要在STM32中配置USART串口通信,并将其连接到HC-05模块的TX和RX引脚。为了与手机APP通信,我们需要设置串口的波特率和数据位、停止位、校验位等参数,以确保通信的准确性。 在STM32的程序中,我们可以使用UART库函数来实现与HC-05的通信。通过在主循环中不断地读取和发送数据,可以实现与蓝牙模块通信。当STM32接收数据时,可以根据需要进行相应的处理,并通过串口将响应数据发送回蓝牙模块。这样,手机APP就可以接收STM32发送的数据,并做出相应的操作。 在手机APP中,我们需要先进行蓝牙设备的配对和连接。一旦与HC-05模块连接成功,我们可以通过APP的界面发送数据STM32,也可以接收STM32发送的数据。这样,就实现了手机APP与STM32之间的无线通信。 综上所述,通过HC-05串口蓝牙模块手机APP,我们可以实现STM32手机之间的通信。这样的无线通信方案在物联网应用中具有广泛的应用前景,可以实现远程控制、数据采集、传感器监测等功能。同时,这种通信方式也大大简化了设备之间的连接,提高了系统的灵活性和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值