使用STM32实现 蓝牙插座

news2024/11/23 21:35:13

硬件介绍

蓝牙模块HC-01,其实之前就用过,使用起来非常简单

继电器模块, (VCC 3.3V)当左侧IN输入低电平时,右侧的ON 和 COM会导通,左上的绿灯会亮,此处充当插座的角色

项目需求

通过蓝牙的串口发送open打开继电器,输入close关闭继电器

项目接线

HC-01 的 TXD和RXD 分别接到单片机的RX1和TX1;

继电器 IN 接入单片机的PB5;(电源接3.3V)

 

蓝牙模块波特率的修改 

由于之前的波特率和现在不同,因此需要先将蓝牙模块接上CH340接到电脑上,使用AT指令修改波特率:

先将串口助手的波特率调回9600,然后发送AT指令:

AT+BAUD=115200,0(这里我淘宝了HC01的介绍)

 

设置完成!然后再将蓝牙插回单片机!

 

使用非中断的方式实现

CubeMX

惯例配置+上节的串口配置(此时不用打开中断)+配置GPIO口

 

Keil

还是先打开Micro-lib,这样才能重写printf函数

#include "stdio.h"
#include "string.h"

int fputc(int a, FILE *f) //一个字符一个字符发送
{
	unsigned char temp[1] = {a};
	HAL_UART_Transmit(&huart1, temp, 1, 0xffff);
	return a;
}

int main(void)
{
  unsigned char buff[20] = {0};

  
  while (1)
  {
		HAL_UART_Receive(&huart1, buff, 19, 100);//19是因为我定义了20位字节的缓冲区,但实际字符串的发送结束会有\0,所以要预留一位,也就是说最多接收19个字符
		if(buff[0] == 'o' && buff[1] == 'p'){ //open
			HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); //导通继电器
		}
		if(buff[0] == 'c' && buff[1] == 'l'){ //close
			HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); //断开继电器
		}
		memset(buff,0,strlen(buff));
		printf("mjm bibi\r\n"); //心跳包
  }

}

实现效果

可见,在蓝牙端可以不断收到心跳包的信息,并且输入open可以打开继电器(close未演示)

使用中断的方式实现

CubeMX

再刚刚的工程文件的基础上,在NVIC打开中断就可以:

Keil 

 还是先打开Micro-lib,这样才能重写printf函数

在这次的代码中,对于接收的串口的指令的代码也可以优化一下,因为之前在做89C52的时候,判断命令都是判断前几个字符是啥,例如open就判断第一个字符是o,第二个字符是p,这种方法在指令少的时候比较管用,但是如果指令多了很容易产生歧义。

而现在,有了流程式的代码,尤其是使用中断的串口时,可以一次接收到完整的指令,这就使得可以对指令的判断使用字符串比较函数strcmp,但是定义的存放数据的是一个字符串变量,因此,只需要对其进行一个强制的转换,使其变成字符串常量,并使用字符串比较,就可以实现精准的命令判断!

#include "stdio.h"

#define UART1_REC_LEN 200 //定义最大接收字节数 200,可根据需求调整
uint8_t buf=0; //串口接收缓存(1字节)
uint8_t UART1_RX_Buffer[UART1_REC_LEN];//接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
 
uint16_t UART1_RX_STA=0; //bit15,接收完成标志 //bit14,接收到0x0d //bit13~0,接收到的有效字节数目

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) // 接收完成回调函数,每收到一个字符(字节)后,就会在这里处理
{
	if(huart->Instance == USART1){ // 判断中断是由哪个串口触发的
		if((UART1_RX_STA & 0x8000) == 0){ // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
			if(UART1_RX_STA & 0x4000){ // 如果已经收到了 0x0d (回车)
				// 则接着判断是否收到 0x0a (换行)
				if(buf == 0x0a){ // 如果 0x0a 和 0x0d 都收到,
					UART1_RX_STA |= 0x8000; //则将 bit15 位 置为1
				}else{ // 否则认为接收错误,重新开始
					UART1_RX_STA = 0;
				}
			}else{ // 如果没有收到 0x0d (回车)
				//则先判断收到的这个字符是否是 0x0d (回车)
				if(buf == 0x0d){ // 是的话则将 bit14 位置为1
					UART1_RX_STA |= 0x4000;
				}else{ // 否则将接收到的数据保存在缓存数组里
					UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf; //因为UART1_RX_STA只有前14位为有效数据,所以缓存数组UART1_RX_Buffer[X]中的X作为16位的二进制数,最高两位的判断应该写在前面代码的判断中,在此处不用
          UART1_RX_STA++;
					if(UART1_RX_STA > UART1_REC_LEN - 1){ //如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
							UART1_RX_STA = 0;
					}
				}
			 }
			}
	HAL_UART_Receive_IT(&huart1, &buf, 1); // 重新开启中断
			
	}
}



int fputc(int a, FILE *f) //一个字符一个字符发送
{
	unsigned char temp[1] = {a};
	HAL_UART_Transmit(&huart1, temp, 1, 0xffff);
	return a;
}


int main(void)
{
  HAL_UART_Receive_IT(&huart1, &buf, 1);//开启中断,并把数据存到buf里

  while (1)
  {
		if(UART1_RX_STA & 0x8000){//不断的判断串口是否接收完成		
			if(!strcmp((const char *)UART1_RX_Buffer,"open")){ //如果strcmp返回0,则说明指令是open 
				HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); //导通继电器
			}
			if(!strcmp((const char *)UART1_RX_Buffer,"close")){ //如果strcmp返回0,则说明指令是close
				HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); //断开继电器
			}
			
			printf("收到数据:"); 
			HAL_UART_Transmit(&huart1, UART1_RX_Buffer, UART1_RX_STA & 0x3fff, 0xffff);// 将收到的数据发送到串口(此处也可以使用printf)
 
			while(huart1.gState != HAL_UART_STATE_READY);// 等待发送完成
			printf("\r\n");
			
			memset(UART1_RX_Buffer,0,strlen(UART1_RX_Buffer)); //这句话得加,不然strcmp的判断方法就不知道为啥会不行
			UART1_RX_STA = 0;// 重新开始下一次接收
		}
		printf("hello mjm\r\n"); //心跳包
		HAL_Delay(1000); 

  }
}

实现效果 

同时!虽然在WINDOWS中的串口中,换行符是“\r\n",但是在ios系统中的串口中,换行符似乎有所区别,这个问题之前就遇到过,但是由于之前51/52的代码中,是直接通过字符串的比对来接收串口的,修改很容易,但是对于STM32来说,尤其是串口中断的处理程序,比较固定,所以解决办法就是直接拿出我的安卓备用机,下载一下蓝牙的APP= =

 在APP中选择“发送新行“,并自定义两个按钮:

 

 

可见,在不断收到间隔1秒的心跳包的同时,只要发送open,就会打开继电器,并收到单片机的串口回传消息;发送close就会立刻关闭继电器,基本实现了蓝牙遥控的插座!

同时,注意到上图中,指令的发送到继电器的响应其实有一点的延时,而我发现这个延迟是main函数中的HAL_Delay造成的,一开始我很奇怪,因为HAL_Delay调用的是滴答定时器,默认的优先级最低,那么串口发生中断的时候,照理说应该会立刻发生响应才对。

但是后来我发现了我的逻辑问题,串口中断的响应优先级的确远高于滴答定时器,但问题是我的继电器响应函数并没有写在串口的中断服务程序之中= =,而是写在了main函数里,那么加入HAL_Delay一秒,进行到半秒的时候来了串口中断,的确是会立刻跳转到串口中断,但是当执行完串口的中断处理函数之后,程序会回到滴答定时器,把剩下的半秒走完才会继续执行下面的代码。

 

 

 

 

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

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

相关文章

JMeter 中 3 种参数值的传递

目录 前言: (一) 从 CSV 文件读取要批量输入的变量 (二) 利用 Cookie 进行值的传递 (三) 利用正则匹配提取上一个接口的返回数据作为下个请求的输入 前言: 在JMeter中,参数值的传递是非常重要的,因为它允许你在测试过程中动态…

Spring 如何解决 Bean 的循环依赖(循环引用)

Component public class A {Autowiredprivate B b;}Component public class B {Autowiredprivate A a;}上面的情况就是 循环依赖 Bean的创建初始化过程如下 如果不采取措施,那么循环依赖就会进入死循环 但 Spring 已经帮我们解决了大部分循环依赖问题 具体是如何解…

RabbitMQ的使用详解

一、什么是MQ 1、什么是MQ MQ(message queue),本质是个队列,FIFO先入先出。只不过队列中放的是message,是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ是一种非常常见的上下游…

EMC案例-接地环路对传导骚扰测试的影响

EMC测试案例分析——接地环路对传导骚扰测试的影响 本文主要就接地环路对传导骚扰测试的影响进行简要举例分析,为我们以后的测试方法提供参考。 Part 1 现象描述 某电子设备在进行传导骚扰测试时,在3MHz左右的频率点出现了超限的情况,其测…

ELK-日志服务【es-安装使用】

目录 【1】安装-配置elasticsearch(01、02、03相同) 端口 【2】安装-配置-启动-Kibana 【3】浏览器访问测试(10.0.0.21:5601) 【4】使用kibana创建、更新、删除es索引、文档 【5】组es集群(投票选举机制&#xf…

用户体验在APP开发中的关键性作用

在 APP开发过程中,如何设计才能让用户感到满意,是非常重要的一点, APP开发公司需要不断地学习新的 APP设计知识,因为只有这样才能设计出令人印象深刻的 APP。对于用户来说,产品的用户体验在很大程度上决定了产品的竞争…

引入头文件#include <iostream>的时候发生了什么?

<iostream> namespace std {extern istream cin;extern ostream cout;extern ostream cerr;extern ostream clog;extern wistream wcin;extern wostream wcout;extern wostream wcerr;extern wostream wclog;};cin是什么&#xff1f; cin extern istream cin; The objec…

elasticsearch集群部署搭建(一)

elasticsearch集群部署搭建&#xff08;一&#xff09; 部署信息JDK安装下载es安装包部署安装创建用户&#xff08;三台机器都执行&#xff09;解压安装包&#xff08;选择一台机器执行&#xff09;修改配置文件&#xff08;三台机器都执行&#xff09; 拷贝分发注册系统服务服…

微信小程序监听页面跳转API

// 放在app.js 里面的onshow生命周期里面wx.onAppRoute((res) > {console.log(路由跳转,res})})

基于B/S架构SaaS服务的实验室信息系统(LIS)

实验室信息系统LIS源码 实验室信息系统&#xff08;Laboratory Information System&#xff09;&#xff0c;简称LIS&#xff0c;是一个全面基于网络化应用&#xff0c;能够帮助用户按照规范内容和规范流程进行多角色、多层次检验信息及资源管理的系统。通过条码管理系统从HIS…

Java并发编程第一弹

1、线程的创建 创建线程的方式有两种&#xff0c; 第一种是通过继承 Thread 类&#xff0c;重写run 方法&#xff1b;第二种是通过实现 Runnable 接口 通过源码发现&#xff0c;创建线程只有一种方式那就是构造 Thread 类&#xff0c;而实现线程的执行单元则有两种方式&…

将node服务打包成可执行文件-PKG

背景 有时我们需要写一些node的服务或者是工具&#xff0c;但这些工具&服务可以运行的前提条件是当前环境需要安装好node&#xff0c;有时候我们把这些工具&服务发送给别人&#xff0c;在别人的电脑中未必有安装好的node版本&#xff0c;即便有也可能不是期望的指定的…

CMU 15-445 -- Join Algorithms - 09

CMU 15-445 -- Join Algorithms - 09 引言Join AlgorithmsJoin Operator OutputI/O Cost AnalysisNested Loop JoinSimple Nested Loop JoinBlock Nested Loop JoinIndex Nested Loop Join小结 Sort-Merge Join小结&#xff1a; Hash JoinBasic Hash Join AlgorithmGrace Hash …

如何获取铁粉

忽然发现我的铁粉从100变成了540&#xff0c;分享下我的经验&#xff0c;我觉得可能是我的机器人经常互动的问题&#xff0c;结合自己的看法和平台大佬的想法一些进行了梳理&#xff1a; 在当今社交媒体时代&#xff0c;吸引和保留铁粉&#xff08;忠实粉丝&#xff09;对于个…

Robocom2021 初赛

收录一下Robocom初赛的屌题&#xff0c;调了我一个多小时&#xff0c;是我菜了 题目详情 - 7-3 打怪升级 (pintia.cn) 题意&#xff1a; Code&#xff1a; #include<bits/stdc.h> using namespace std;int n, m, a, b, c, d, q, p; int f[1005][1005];const int N 2…

Vector - CANoe - 测试报告设置

file:///C:/Program%20Files/Vector%20CANoe%2015/Help01/CANoeCANalyzerHTML5/CANoeCANalyzer.htm#Topics/CANoeCANalyzer/Windows/TestConfigurations/TCConfigTC.htm 前面有过介绍&#xff0c;我们常用的测试报告还是以XML/HTML格式来生成测试报告&#xff0c;而对于XML/HTM…

【洛谷】P1342 请柬(正反建图+dijkstra)

1&#xff1a;思考&#xff1a; 从1到所用顶点简单&#xff08;单源最短路径。&#xff09;&#xff0c;重点在怎么解决所用点到1&#xff08;单终点最短路径&#xff09; 答案&#xff1a;反向建图使&#xff08;单终点最短路径→单源最短路径。&#xff09; 复杂度&#xf…

基于SpringBoot的网上订餐系统【附ppt和开题|万字文档(LW)和搭建文档】

主要功能 前台登录&#xff1a;前台登录&#xff1a; ①首页&#xff1a;菜品信息推荐、菜品信息展示、查看更多 ②菜品信息&#xff1a;菜品分类、菜品名称查询、食材查询、菜品详情、下单提交 ③个人中心&#xff1a;可以查看自己的信息、我的订单、我的地址 后台登录&#…

【杨氏矩阵】

这篇文章的对应思维导图为&#xff1a;思维导图 思维导图对应代码&#xff1a; //杨氏矩阵 #include<stdio.h>//void ysjz1(int a[3][3],int k) { // int x 0; // int y 2; // while (x < 2 && y > 0) { // if (a[x][y] > k) { // y--; // } // …

算法训练营第三十六天||● 435. 无重叠区间 ● 763.划分字母区间 ● 56. 合并区间

● 435. 无重叠区间 解法1&#xff1a; 本题其实和452.用最少数量的箭引爆气球 (opens new window)非常像&#xff0c;弓箭的数量就相当于是非交叉区间的数量&#xff0c;只要把弓箭那道题目代码里射爆气球的判断条件加个等号&#xff08;认为[0&#xff0c;1][1&#xff0c;…