STM32 基于蓝牙模块HC-05实现手机与单片机通信

一、HC-05蓝牙模块使用

1.1接线

HC-05供电电压为3.6V~6V。通常使用5V供电。其余的RXD接TXD,TXD接RXD,CND接GND即可。

1.2配置模式

HC-05可以使用AT指令进行配置。在配置之前,首先需要让模块进入配置模式。HC-05有一个小按钮。按住小按钮再给蓝牙模块上电,蓝牙模块进入配置模式,此时模块上自带的LED会慢速闪烁。进入配置模式后,就可以用AT指令来配置我们的HC-05了。配置时,用USB转TTL连接HC-05,用串口调试助手发送AT指令进行配置。需要注意的是,HC-05配置模式的波特率固定为38400,如果你给HC-05发送指令,没有收到回复,记得检查一下串口调试助手的波特率是否正确。

AT
检查HC-05模块连接是否正常,HC-05收到后会回复“OK”
AT+NAME=名字
配置HC-05的名字,配置成功后会返回“OK”
AT+NAME?
询问HC-05的名字。发送后会收到“+NAME:“名字””,换行加“OK”
AT+PSWD=密码
配置HC-05密码,配对时需要用到。配置成功后,会收到“OK”
AT+PSWD?
询问HC-05配对密码。发送后会收到“+PSWD:991102”,换行加“OK”
AT+UART=波特率,停止位,校验
设置HC-05的波特率,其中停止位0表示一位停止位,为1表示两位停止位。校验位为0表示无校验,为1表示奇校验,为2表示偶校验。比如设置115200的波特率,一位停止位,无校验。发送“AT+UART=115200,0,0”即可。配置成功后会返回“OK”
AT+UART?
询问HC-05波特率。发送后会收到“+UART:波特率,停止位,校验”,换行加“OK”

配置完成功后,断电重新上电,HC-05按照配置好的名字,配对密码和波特率开始工作。此时LED快闪。

1.3手机端上位机

参考:提高开发效率-蓝牙调试器 - 简书 (jianshu.com)

二、STM32串口配置

注意:串口波特率应该与蓝牙AT模式配置的波特率一致。

HAL库正常配置即可,打开串口中断。

协议:帧头(1Byte)+ data(2Byte)+data字节之和低八位校验(1Byte)+ 帧尾(1Byte

这是由上位机决定的,发送这样的数据给手机,手机才能解析。

main.c全部内容:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t rxbuff[5] = {0};
uint8_t txbuff[5] = {0};
uint8_t content[3] = {0};
uint8_t checksum = 0;
uint8_t rx_flag = 0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* 校验:取数据之和低八位 */
/*str:数据帧内容 data_length:数据长度 */
uint8_t CheckSum(uint8_t *str, int data_length)
{
    int CheckSum_Value = 0;
    int i = 0;
    for(i = 0; i < data_length; i++)
    {
       CheckSum_Value += str[i];
    }
    CheckSum_Value = CheckSum_Value & 0xFF;
    return (uint8_t)CheckSum_Value;
}
//截取字符  存在问题
//eg src = hello,world_ substring(src, dest,6, 5)
//表示从下标6的字符w开始,复制5个字符到dest ,即world
void substring(uint8_t *src, uint8_t *dest, int start, int length) 
{
    if (start < 0 || length <= 0 || start + length > strlen((char*)src)) 
    {
        //printf("Invalid parameters!\n");
        return;
    }
    // 使用strncpy函数复制子字符串到目标字符串
    strncpy((char*)dest, (char*)src + start, length);
    // 在目标字符串的末尾添加空字符,必须吗?
    dest[length] = '\0';
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, rxbuff, sizeof(rxbuff));// 启动异步接收
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    if(rx_flag == 1)
		{
			rx_flag = 0;
			HAL_UART_Transmit_IT(&huart1, txbuff, sizeof(txbuff)); //5 Bytes
		}	
	}
}
  /* USER CODE END 3 */


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
//只有帧头帧尾或者校验和出问题,会传回FF,否则会传回发送的内容
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance == USART1)    //检查是否为USART1串口
  {
		if(rxbuff[0] == 0xA5 && rxbuff[4] == 0x5A )//检查帧头帧尾 
		{
			//substring(rxbuff,content,1,2);//截取数据帧中的data内容部分
			content[0] = rxbuff[1];
			content[1] = rxbuff[2];
			checksum = CheckSum(content, 2);//计算data内容部分的校验和低八位
			if(rxbuff[3] == checksum)//检查校验和通过 rxbuff[3] == 0x01 checksum
			{
				/******组装tx数据帧*******/
				txbuff[0] = 0xA5;
				txbuff[4] = 0x5A;
        //for(int i = 0;i<2;i++)//数据内容放入txbuff数据帧
				//{
				//	txbuff[i+1] = content[i];
				//}
				txbuff[1] = rxbuff[1];
				txbuff[2] = rxbuff[2];
        txbuff[3] = checksum;
				/******组装tx数据帧*******/
				if(content[0] == 0x01 )
			    HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
				if(content[1] == 0x02 )
			    HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
			}
			else //校验和检查未通过,全FF
			{
				txbuff[0] = 0xA5;
				txbuff[4] = 0x5A;
				txbuff[1] = 0xFF;
				txbuff[2] = 0xFF;
				txbuff[3] = 0xFF;
			}
		}
    else//帧头帧尾不对,全FF
		{
			txbuff[0] = 0xA5;
			txbuff[4] = 0x5A;
			txbuff[1] = 0xFF;
			txbuff[2] = 0xFF;
			txbuff[3] = 0xFF;
		}			
		 // 启动下一次异步接收	
		rx_flag = 1;
    HAL_UART_Receive_IT(&huart1, rxbuff, sizeof(rxbuff)); //5 Bytes		
		//HAL_UART_Transmit_IT(&huart1, txbuff, sizeof(txbuff)); //这句话最好不要放在rx回调函数,会传全0
//		for(int i = 0;i < 4;i++) 加上以后,发回的数据全是0
//		{
//			rxbuff[i] = 0;
//			txbuff[i] = 0;
//		}
		//memset(rxbuff,0,4);加上以后,发回的数据全是0
		//memset(txbuff,0,4);
  }
}

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
		HAL_UART_Transmit_IT(&huart1, rxbuff, 4);
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

注意:sizeof(rxbuff)这里是5,也就是接受五个字符,才会执行一次Rxcallback回调函数。

三、现象

当上位机传输符合协议的数据帧时,相应LED翻转电平,同时发回与一样的数据帧给上位机。如果不符合协议,data与校验位发回FF。

附上透传的原始数据帧。最后一条不符合协议,而前面的符合协议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值