STM32F103C8T6+HC05蓝牙模块

摘要:

手机软件是“蓝牙调试器”,可以自己设计gui界面。我将在代码后面介绍如何创建gui界面。

蓝牙波特率9600。

单片机采用USART3。PB10连接蓝牙的RX,PB11连接蓝牙的TX。蓝牙是5v供电(3.3v也可以)。

下面是代码

Serial.c

#include "Serial.h"

//接收
#define E_START                    0  	 //准备成功
#define E_OK                       1   	//成功
#define E_FRAME_HEADER_ERROR       2   	//包头错误
#define E_FRAME_RTAIL_ERROR        3  	 //包尾错误
#define LINE_LIN                   12  	//数据长度

#define HEADER 0xA5						//包头
#define TAIL 0x5A						//包尾

//发送
#define USART_TX_LEN 14					//数据包大小

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

uint8_t uart_flag;                     //接收标志
vu8 RX_lanya[12];						//接收大小

extern int test_number;

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

void Serial_Init(void)		//初始化
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//	GPIO_PinRemapConfig(GPIO_PartialRemap_USART3,ENABLE);//USART3完全重映射
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;		//TX	需要连接RX
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;		//RX	需要连接TX
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;		//注意波特率要和蓝牙模块匹配	38400	JDY31-SPP	115200
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART3, &USART_InitStructure);
	
	//配置中断
	
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
	
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART3, ENABLE);
}

void Serial_SendByte(uint8_t Byte)		//发送字节
{
	USART_SendData(USART3, Byte);
	while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)		//发送数组
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)		//发送字符串
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)		//发送数字
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

//int fputc(int ch, FILE *f)			//移植printf
//{
//	Serial_SendByte(ch);			//与usart文件里的重定义重复注释掉
//	return ch;
//}

void Serial_Printf(char *format, ...)		//移植printf
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}


//和手机相关联。

/*接收数据*/
void get_slave_data(uint8_t data)
{
    static uint8_t uart_num=0;
    RX_lanya[uart_num++]=data;		//?
    if(1==uart_num)
    {
        //接收到第一个字节不是0xA5,包头错误
        if(0XA5!=RX_lanya[0])
        {
            uart_num=0;
            uart_flag=E_FRAME_HEADER_ERROR;
        }
    }
    if(LINE_LIN==uart_num)
    {
        uart_flag=E_OK;
        //接收到最后一个字节是0X5A
        if(0X5A==RX_lanya[LINE_LIN-1])
        {
            uart_flag=E_OK;
        }
        else    //接收到的最后一个字节不为ox5A,包尾错误
        {
            uart_flag=E_FRAME_RTAIL_ERROR;
        }
        uart_num=0;
    }
 
}
 
int key1=0,key2=0,key3=0,key4=0,key5=0;
 
/*解析数据*/
/*数据包构成:包头(1字节)+字节变量(1字节)+4个段整形变量(共8个字节)+校验位(1字节)+包尾(1字节),共12个字节*/
void lanya_receive(void)
{
	key1=RX_lanya[1];
	
	key2=((int)RX_lanya[3]<<8)|RX_lanya[2];
	
	key3=((int)RX_lanya[5]<<8)|RX_lanya[4];
	
	key4=((int)RX_lanya[7]<<8)|RX_lanya[6];
	
	key5=((int)RX_lanya[9]<<8)|RX_lanya[8];
	
}

//发送数据
//计算校验位
uint8_t checksum(void)
{
	uint8_t checksum = 0;
	for (int i = 1; i <= (USART_TX_LEN-3); ++i) 
	{
		checksum += USART_TX_BUF[i];
	}
	checksum &= 0xff;
	return checksum;
}
//数据类型转变
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);
}

//组合数据包
/*数据包构成:包头(1字节)+1个字节变量(1字节)+1个短整形变量(2个字节)+1个整形变量(4个字节)+1个浮点形变量(4个字节)+校验位(1字节)+包尾(1字节),共14个字节*/
void lanya_transmit(void) 
{
	char x = 0x10;			//-128~127
	short y = 0x02;			//-32768~32767
	int z = 0x09;			//4字节
	float f = 20.5;			//4字节
	
	USART_TX_BUF[0] = HEADER;		//包头

	USART_TX_BUF[1] = (uint8_t)x;
	
	Short_to_Byte(y,&USART_TX_BUF[2]);
	
	Int_to_Byte(z,&USART_TX_BUF[4]);

	Float_to_Byte(f,&USART_TX_BUF[8]);

	//计算校验和
	USART_TX_BUF[12] = checksum();
		
	USART_TX_BUF[13] = TAIL;		//包尾

	//通过串口1发送
	Serial_SendArray(USART_TX_BUF,14);

}


void USART3_IRQHandler(void)
{
    if(USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET)
    {
	   USART_ClearITPendingBit(USART3,USART_IT_RXNE);
	   Serial_RxData = USART_ReceiveData(USART3);		//返回 USART2 最近接收到的数据
	   Serial_RxFlag = 1;
	   get_slave_data(Serial_RxData);       //获取数据
			if(uart_flag==1)
			{
				uart_flag=0;
				lanya_receive();   //数据解析
			}
    }
}


Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H
#include "sys.h"
#include <stdio.h>
#include <stdarg.h>

extern int key1,key2,key3,key4,key5;

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);


//和手机相关联	GUI
void get_slave_data(uint8_t data);
void lanya_receive(void);
uint8_t checksum(void);
void Int_to_Byte(int i,uint8_t *byte);
void Float_to_Byte(float f,uint8_t *byte);
void Short_to_Byte(short s,uint8_t *byte);

void lanya_transmit(void); 

#endif

main.c

#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "bmp.h"
#include "Serial.h"

//HC-05波特率匹配9600
//引脚对应
//手机软件GBK编码

int main(void)
{
	delay_init();
	OLED_Init();
	OLED_ColorTurn(0);//0正常显示,1 反色显示
	OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
	OLED_Clear();
	Serial_Init();
	uint8_t Direction;

	while(1)
	{
		
		if (Serial_GetRxFlag() == 1)		//判断是否收到数据
		{
			Direction = Serial_GetRxData();		//读取数据
			OLED_ShowChar(0,0,Direction,16,1);
			OLED_Refresh();
			Serial_SendByte(Direction);		//将数据回传到电脑
		}
		
	}
}

以上代码可以实现基础的蓝牙收发。实现简单的控制效果。

下面来讲一下蓝牙调试器的GUI制作。

1、首先打开蓝牙调试器

2、打开后刷新一下,连接好蓝牙模块HC-05,连接后左上角会显示已连接

3、选择下方的“专业调试”,新建工程,确定

4、工程创建好后我们要根据我们的需要进行通信设置。我以上面的代码为例

发送数据包:
/*解析数据*/
/*数据包构成:包头(1字节)+字节变量(1字节)+4个段整形变量(共8个字节)+校验位(1字节)+包尾(1字节),共12个字节*/

(蓝牙的发送对应单片机的接收,所以注释是解析数据)

接收数据包:
//组合数据包
/*数据包构成:包头(1字节)+1个字节变量(1字节)+1个短整形变量(2个字节)+1个整形变量(4个字节)+1个浮点形变量(4个字节)+校验位(1字节)+包尾(1字节),共14个字节*/

最后是通细腻模式的设置,选择“仅操作控件时发送”

在数据包结构设置里的右上角“?”。打开可以看到如下内容。

5、编辑控件,根据需要选择适合的控件,并设置好参数。

6、到此蓝牙手机发送部分就完成啦,可以打开观察实验现象了。

//主函数
int main(void)
{
	delay_init();
	OLED_Init();
	OLED_ColorTurn(0);//0正常显示,1 反色显示
	OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
	OLED_Clear();
	Serial_Init();

	uint8_t Direction;

	while(1)
	{
		
		OLED_ShowNum(16,16,key2,1,16,1);    //这里的key2对应short0

		OLED_Refresh();	
		
		if (Serial_GetRxFlag() == 1)		//判断是否收到数据
		{
			Direction = Serial_GetRxData();		//读取数据
			OLED_ShowChar(0,0,Direction,16,1);
			OLED_Refresh();
			Serial_SendByte(Direction);		//将数据回传到电脑
		}
		
	}
}

7、单片机发送数据手机接收

修改下面的数据位你需要的变量。

这里我将发送数据包的short位设置为test_number。

添加下面的控件:

选择链接的数据 

调整合适的大小

实验现象:

发送一个锯齿波。

代码。主要是修改一下main函数。另外记得在serial.c里声明extern int test_number;。

main.c

#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "bmp.h"
#include "Serial.h"

//HC-05波特率匹配9600
//引脚对应
//手机软件GBK编码

//实现收发

//加入gui
int test_number=0;

int array[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int key_1=0;

int main(void)
{
	delay_init();
	OLED_Init();
	OLED_ColorTurn(0);//0正常显示,1 反色显示
	OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
	OLED_Clear();
	Serial_Init();
//	test_0();		//开机动画
	uint8_t Direction;

	while(1)
	{	
		lanya_transmit();
		
		if(key_1==0)
		{
			test_number++;
			if(test_number==60)
			{
				key_1=1;
			}			
		}
		else if(key_1==1)
		{
			test_number--;
			if(test_number==0)
			{
				key_1=0;
			}	
		}
		
		OLED_ShowNum(16,16,key2,1,16,1);
		OLED_Refresh();
		
	
		if (Serial_GetRxFlag() == 1)		//判断是否收到数据
		{
			Direction = Serial_GetRxData();		//读取数据
			OLED_ShowChar(0,0,Direction,16,1);
			OLED_Refresh();
			Serial_SendByte(Direction);		//将数据回传到电脑
		}
		
	}
}

sys.h

#ifndef __SYS_H
#define __SYS_H	 
#include "stm32f4xx.h" 
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F407开发板
//系统时钟初始化	
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/5/2
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//修改说明
//无
// 


//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS		0		//定义系统文件夹是否支持UCOS
																	    
	 
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入

//以下为汇编函数
void WFI_SET(void);		//执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void);	//开启所有中断
void MSR_MSP(u32 addr);	//设置堆栈地址 
#endif











sys.c

#include "sys.h"  

#include "Motor_T.h"
#include "Motor_S.h"

#include "Interpolation_Linear.h" 
#include "Interpolation_Arc.h" 

extern inter_pol_def_arc g_pol_par_arc; 				//圆弧插补模式
extern st_motor_status_def  g_motor_sta_linear;			//直线插补	运动模式
extern inter_pol_def g_pol_par_linear;					//直线插补模式
extern st_motor_status_def_arc  g_motor_sta_arc;		//圆弧插补	运动模式

//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI  
__asm void WFI_SET(void)
{
	WFI;		  
}
//关闭所有中断(但是不包括fault和NMI中断)
__asm void INTX_DISABLE(void)
{
	CPSID   I
	BX      LR	  
}
//开启所有中断
__asm void INTX_ENABLE(void)
{
	CPSIE   I
	BX      LR  
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
	MSR MSP, r0 			//set Main Stack value
	BX r14
}



void TIM3_IRQHandler(void)
{
	//T
	speed_decision_T();
	
	//	S
	speed_decision_S();
}






void TIM2_IRQHandler(void)
{
//	g_pol_par_linear.moving_mode
	if(g_pol_par_linear.moving_mode == LINE)
	{
		straight_speed_decision();
	}
	
	else if(g_pol_par_arc.moving_mode == ARC)
	{
		circle_speed_decision();
	}

}







8、工程文件下载

看到这里了,对于有需要的同学可以到github上下载工程文件。

LYYSXT/HC05 (github.com)

打开github需要下载加速器,下面是加速器的下载连接。

GitHub与GitHubDesktop的使用_github desktop和github区别-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_69752994/article/details/133298751

我在CSDN的资源里也上传了工程文件,目前正在审核,审核通过后会发布链接在评论区。

  • 16
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 44
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值