蓝牙模块—BLE-CC41-A

news2024/9/22 11:19:27

1. 蓝牙的特点


蓝牙模块采用的 TI 公司设计的 CC2541芯片,主要面向低功耗蓝牙通信方案,该模块的工作频段为 2.4Ghz,这个频段属于国际通用频段

注意:蓝牙集成了一个状态指示灯,LED灯如果均匀慢速闪烁,就表示蓝牙未连接,如果LED灯常亮,表示已连接

2. 蓝牙的模式

蓝牙具有两种工作模式,一种是 AT 指令模式,一种是 数据透传模式,两种模式的特点如下

a. AT 指令模式

        AT指令模式指的是蓝牙未连接的工作模式,在该模式可以获取或配置蓝牙的参数(蓝牙名字、蓝牙密码、蓝牙地址......),需要利用固定的 AT 指令,注意不同公司设计的蓝牙模块的AT 指令大同小异,具体指令参考手册

        代码里面:换行字符结尾 (\r\n)         串口调试助手里面:回车结尾


练习:编写程序,利用电脑的串口调试助手发送对应的 AT 指令,设置蓝牙模块的参数信息

如果USART1的波特率和USART3不一致,就可能会出现丢包,两种解决方法:

        (1) 波特率设置一致

        (2) 把接收的数据都接收完,再发送给其他串口

usart.h

#ifndef __USART_H__
#define __USART_H__

#include "stm32f4xx.h"
#include "stdio.h"

/*
	串口1的初始化函数
	@baudrate:串口传输的波特率
*/
void USART1_Init(int baudrate);

/*
	串口3的初始化函数
	@baudrate:串口传输的波特率
*/
void USART3_Init(int baudrate);


// 发送一个字节
void USART_SendByte(USART_TypeDef *USARTx, uint16_t data);

// 发送一个字符串
void USART_SendDatas(USART_TypeDef *USARTx, const char *str);

// 蓝牙的初始化,只需要调用一次
// void BLE_Init(void);

// 将printf函数重定向到串口  注意:windows 中换行符为 \r\n
int fputc(int c, FILE *stream);

#endif

usart.c

#include "usart.h"
#include "systick.h"

/*
	串口1的初始化函数
	@baudrate:串口传输的波特率
*/
void USART1_Init(int baudrate) {
	
	// (1) GPIO 引脚初始化
	// a. 使能 GPIO 分组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	// b. 初始化 GPIO
	// PA9 Tx   PA10 Rx
	GPIO_InitTypeDef g;

	g.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
	g.GPIO_Mode = GPIO_Mode_AF;
	g.GPIO_Speed = GPIO_Speed_2MHz;
	g.GPIO_OType = GPIO_OType_PP; // 输出推挽
	g.GPIO_PuPd = GPIO_PuPd_NOPULL;
	
	GPIO_Init(GPIOA, &g);

	// c. 配置GPIO复用功能
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
	
	// (2) USART配置
	// a. 使能 USART 分组时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	// b. 初始化配置USART
	USART_InitTypeDef u;

	// 波特率
	u.USART_BaudRate = baudrate;
	// 指定数据帧数据位,也就是传输字长 = 数据位数 + 校验位数
	u.USART_WordLength = USART_WordLength_8b;
	// 指定停止位长度
	u.USART_StopBits = USART_StopBits_1;
	// 指定校验方式 不要校验
	u.USART_Parity = USART_Parity_No;
	// 指定串口模式  全双工
	u.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 指定硬件控制流   不要硬件流控
	u.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	
	USART_Init(USART1, &u);
	
	// (3) 中断配置
	
	// a. 中断源的控制(中断控制位使能)
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

	// b. 配置NVIC
	NVIC_InitTypeDef v;

	v.NVIC_IRQChannel = USART1_IRQn;
	v.NVIC_IRQChannelPreemptionPriority = 2;
	v.NVIC_IRQChannelSubPriority = 2;
	v.NVIC_IRQChannelCmd = ENABLE;

	NVIC_Init(&v);
	
	// (4) 使能串口
	USART_Cmd(USART1, ENABLE);
}

/*
	串口3的初始化函数
	@baudrate:串口传输的波特率
*/
void USART3_Init(int baudrate) {
	
	// (1) GPIO 引脚初始化
	// a. 使能 GPIO 分组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	// b. 初始化 GPIO
	// USART3_TX --- PB10    USART3_RX --- PB11
	GPIO_InitTypeDef g;

	g.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	g.GPIO_Mode = GPIO_Mode_AF;
	g.GPIO_Speed = GPIO_Speed_2MHz;
	g.GPIO_OType = GPIO_OType_PP; // 输出推挽
	g.GPIO_PuPd = GPIO_PuPd_NOPULL;
	
	GPIO_Init(GPIOB, &g);

	// c. 配置GPIO复用功能
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_USART3);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_USART3);
	
	// (2) USART配置
	// a. 使能 USART 分组时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	
	// b. 初始化配置USART
	USART_InitTypeDef u;
	
	// 波特率
	u.USART_BaudRate = baudrate;
	// 指定数据帧数据位,也就是传输字长 = 数据位数 + 校验位数
	u.USART_WordLength = USART_WordLength_8b;
	// 指定停止位长度
	u.USART_StopBits = USART_StopBits_1;
	// 指定校验方式 不要校验
	u.USART_Parity = USART_Parity_No;
	// 指定串口模式  全双工
	u.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 指定硬件控制流   不要硬件流控
	u.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	
	USART_Init(USART3, &u);
	
	// (3) 中断配置
	
	// a. 中断源的控制(中断控制位使能)
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);

	// b. 配置NVIC
	NVIC_InitTypeDef v;

	v.NVIC_IRQChannel = USART3_IRQn;
	v.NVIC_IRQChannelPreemptionPriority = 2;
	v.NVIC_IRQChannelSubPriority = 2;
	v.NVIC_IRQChannelCmd = ENABLE;

	NVIC_Init(&v);
	
	// (4) 使能串口
	USART_Cmd(USART3, ENABLE);
}

// 发送一个字节
void USART_SendByte(USART_TypeDef *USARTx, uint16_t data) {
	// 发送
	USART_SendData(USARTx, data);

	// 等待发送寄存器为空事件产生
	while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) != SET);

	// 清除发送寄存器为空事件标志
	USART_ClearFlag(USARTx, USART_FLAG_TXE);
}

// 发送一个字符串
void USART_SendDatas(USART_TypeDef *USARTx, const char *str) {
    const char *s = str;
	while (*s) {
		USART_SendByte(USARTx, *s);
		s++;
	}	
}

/*
	串口1 的中断服务函数
*/
void USART1_IRQHandler(void) {
	
	// 如果产生了接收中断  RXNE 事件产生
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) {

		uint8_t RecvData = USART_ReceiveData(USART1);
		
		USART_SendByte(USART3, RecvData);
		
		// 清除中断标志
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

/*
	串口3 的中断服务函数
*/
void USART3_IRQHandler(void) {
	
	// 如果产生了接收中断  RXNE 事件产生
	if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET) {

		uint8_t RecvData = USART_ReceiveData(USART3);
		
		USART_SendByte(USART1, RecvData);
		
		// 清除中断标志
		USART_ClearITPendingBit(USART3, USART_IT_RXNE);
	}
}

// 蓝牙的初始化,只需要调用一次
//void BLE_Init(void) {
//	
//	USART_SendDatas(USART3, "AT\r\n"); // 发送测试指令
//	delay_ms(500);
//	
//	USART_SendDatas(USART3, "AT+NAME666\r\n"); // 设置蓝牙名称
//	delay_ms(500);
//	
//	USART_SendDatas(USART3, "AT+LADDR\r\n"); // 获取蓝牙地址
//	delay_ms(500);
//	
//	USART_SendDatas(USART3, "AT+RESET\r\n"); // 复位蓝牙重启
//	delay_ms(500);
//	
//	printf("ble init success\r\n");	
//}

// 将printf函数重定向到串口  注意:windows 中换行符为 \r\n
int fputc(int c, FILE *stream) {

	USART_SendData(USART1, c & 0xFF);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
	
	// 清除发送寄存器为空事件标志
	USART_ClearFlag(USART1, USART_FLAG_TXE);
	
   	return 0;
}

main.c

#include "stm32f4xx.h"
#include "usart.h"

int main(void) {

	NVIC_PriorityGroupConfig(2);	
	
	USART1_Init(9600);
	
	USART3_Init(9600);
	
	while (1);
}

b. 数据透传模式
        数据透传模式指的是蓝牙已经被手机连接,该模式下蓝牙就相当于一根透明的串口线,蓝牙只负责把数据发送到目的地,不对数据进行处理

练习:编写代码,把超声波获取的距离每隔 2000 ms 发送到手机端进行查看

#include "stm32f4xx.h"
#include "usart.h"
#include "systick.h"
#include "HC_SR04.h"
#include "systick.h"

int main(void) {

	NVIC_PriorityGroupConfig(2);	
	
	USART3_Init(9600);
	
	HC_SR04_Init();
	
	while (1) {
		
		uint32_t distance = HC_SR04_GetDistance();

        // 方法一:重定向串口3  printf

		// 方法二:
		char res[50] = {0};
		sprintf(res, "distance = %d mm", distance);
		USART_SendDatas(USART3, res);
		
		delay_ms(2000);
	}
}

扩展:

        (1) 编写代码,可以利用超声波检测障碍物的距离,根据不同的距离进行提示,如果距离小于 30cm 则蜂鸣器叫的声音频率较高,如果距离大于 30cm 并小于 60 cm,则蜂鸣器声音较小,如果距离大于60cm,则蜂鸣器不响。可以利用手机 APP 设置距离的上下限,比如发送"SetDis=15cm\r\n",则距离小于 15cm 时蜂鸣器叫的声音频率较高

        思考:如何对接收到的字符串进行处理 "SetDis=15cm"               

                提示:strstr   strtok       字符串转整型:atoi     整型转字符串:sprintf

        

        比如利用手机 APP 发送特定的数据包,格式为"beep=on\r\n",就可以让蜂鸣器鸣叫,发送"beep=off\r\n",可以让蜂鸣器不叫
       

        (2) 利用手机APP,控制距离的两个阈值  dis1  +  -    dis2 + -

如果程序中定义的局部数组太多太大了,需要在启动文件中修改栈空间大小

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2139125.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

接口测试从入门到精通项目实战

视频网址:2024最新接口测试从入门到精通项目实战(全套接口测试教程)_哔哩哔哩_bilibili 接口架构设计分析 http协议详解 JMeter 目录介绍 常用组件 执行接口测试 接口文档:tlias智能学习辅助系统接口文档-V1.0-CSDN博客 实战 前…

HTB-Lame(msf利用)

前言 各位师傅大家好,我是qmx_07,今天给大家讲解 初学者渗透路径 第一个靶机Lame 渗透过程 信息搜集 服务器开放了21FTP端口、22SSH端口、445SMB端口 利用MSF模块 登录主机 工具介绍:Metasploit Framework(简称 MSF&#xf…

RK3562/3588系列之6—yolov5模型的部署

RK3562/3588系列之6—yolov5模型的部署 1.yolov5模型训练2.训练好的模型转成onnx格式3.模型从onnx格式转RKNN3.1 onnx2rknn.py3.2 onnx2rknn.py3.3 直接使用rknn.api3.4 rknn_model_zoo中的转换代码3.5 LubanCat-RK系列板卡官方资料4.RK NPU c++推理4.1交叉编译4.2 开发板执行编…

校园失物招领小程序

校园失物招领小程序 weixin167校园失物招领小程序ssm 目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 JSP技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系…

清理C盘缓存,如何针对Windows10系统,专业地调整和优化C盘缓存设置

在Windows10系统中,合理地管理C盘(通常是系统盘)的缓存设置,对于保持系统性能、提升响应速度以及避免磁盘空间不足等问题至关重要。缓存主要涉及到系统文件、临时文件、应用程序缓存等多个方面。下面将详细介绍如何针对Windows10系…

【webpack4系列】webpack进阶用法(三)

文章目录 自动清理构建目录产物PostCSS插件autoprefixer自动补齐CSS3前缀移动端CSS px自动转换成rem静态资源内联多页面应用打包通用方案使用sourcemap提取页面公共资源基础库分离利⽤ SplitChunksPlugin 进⾏公共脚本分离利⽤ SplitChunksPlugin 分离基础包利⽤ SplitChunksPl…

UVA1395 Slim Span(最小生成树)

*原题链接*(洛谷) 非常水的一道题。看见让求最小边权差值的生成树,很容易想到kruskal。 一个暴力的想法是以每条边为最小边跑一遍kruskal,然后统计答案。时间复杂度,再看题中很小的数据范围和3s的时限。最后还真就过了。 不过我天真的想了…

【机器学习】11——矩阵求导

机器学习11——矩阵求导 打公式不太好标注,全图警告!!! 文章目录 机器学习11——矩阵求导1.1标量对向量1.2标量对矩阵2.1向量对标量2.2向量对向量2.3向量对矩阵 1.1标量对向量 1.2标量对矩阵 X是m*n的矩阵,不严谨&am…

代码随想录Day 44|leetcode题目:1143.最长公共子序列、1035.不相交的线、53. 最大子序和、392.判断子序列

提示:DDU,供自己复习使用。欢迎大家前来讨论~ 文章目录 题目题目一:1143.最长公共子序列解题思路: 题目二: 1035.不相交的线解题思路: 题目三:53. 最大子序和解题思路 题目四:392.判…

【白话树】之 二叉树

快速导航 一、二叉树的基本概念1、 二叉树定义2、常见术语3、基本操作1)创建:2)插入与删除: 4、常见类型1)满二叉树(完美二叉树)2)完全二叉树3)完满二叉树4)平…

Mysql InnoDB 存储引擎简介

InnoDB 存储引擎是 Mysql 的默认存储引擎,它是由 Innobase Oy 公司开发的 Mysql 为什么默认使用 InnoDB 存储引擎 InnoDB 是一款兼顾高可靠性和高性能的通用存储引擎 在 Mysql 5.5 版本之前,默认是使用 MyISAM 存储引擎,在 5.5 及其之后版…

WEB打点

目录 web打点概述打点流程打点中的问题getshell手法汇总web打点批量检测端口扫描POC扫描指纹识别漏洞扫描 手工检测开源框架漏洞通用框架漏洞基础web漏洞商用系统漏洞 WAF绕过waf分类常见waf拦截界面WAF绕过思路侧面绕:适合云WAF直面WAF web打点概述 打点流程 资产…

远程Linux网络连接( Linux 网络操作系统 04)

接下来我们准备开始进入Linux操作系统的第二个模块的学习,不过在学习之前我们需要对如下进行简单的配置,通过外接辅助软件MobaXterm来进行虚拟操作系统的访问。接下来的课程我们会一直在MobaXterm中进行命令和相关知识的学习。 一、准备阶段 1.1 软件 …

安装配置filebrowser

安装配置filebrowser ​ 这章就简单搞个工具用一下,这个工具就是一个像安卓软件一样的文件浏览器,可以设置用户权限啥的,挺好用的下面直接粘的安装步骤,注意一下配置别错了就行,json文件和命令配置要一样。访问效果放…

逻辑回归原理

本文主要介绍了逻辑回归的原理和应用,包括从线性回归到逻辑回归的转换、二元逻辑回归的模型和损失函数、优化方法以及正则化等内容。以下是内容的详细叙述: 1. 从线性回归到逻辑回归 线性回归模型:线性回归是找出输出特征向量Y与输入样本矩阵…

无线通信感知/雷达系统算法专业技术栈

无论是在工业界还是在学业界,无线通信感知一体化都是一个热门的方向,作为一个24届毕业生,刚好处于行业当中,就总结一下自己浅薄认知下,自己觉得已经掌握或者应该掌握的技术栈和专业能力,与大家共勉。 Rada…

CS61C 2020计算机组成原理Lab01-数字表示,溢出

1. Exercise 1 :See what you can C # 用gcc 来编译可执行文件如program.c gcc program.c # 就可以得到一个executable file named a.out. ./a.out# 如果想给这个可执行文件命名,则使用 -o gcc -o program program.c ./program# 使用-g 能得到一个 可执行程序的deb…

微服务保护之熔断降级

在微服务架构中,服务之间的调用是通过网络进行的,网络的不确定性和依赖服务的不可控性,可能导致某个服务出现异常或性能问题,进而引发整个系统的故障,这被称为 微服务雪崩。为了防止这种情况发生,常用的一些…

K8s 之控制器的定义及详细调用案例

什么是控制器 官方文档: https://v1-30.docs.kubernetes.io/zh-cn/docs/concepts/workloads/controllers/ 控制器也是管理pod的一种手段 自主式pod:pod退出或意外关闭后不会被重新创建控制器管理的 Pod:在控制器的生命周期里,始…

《深度学习》PyTorch 手写数字识别 案例解析及实现 <下>

目录 一、回顾神经网络框架 1、单层神经网络 2、多层神经网络 二、手写数字识别 1、续接上节课代码,如下所示 2、建立神经网络模型 输出结果: 3、设置训练集 4、设置测试集 5、创建损失函数、优化器 参数解析: 1)para…