【STM32项目】基于STM32+RTOS音频光通信设计与实现(完整工程资料源码)

news2024/9/22 23:38:49

基于STM32音频光通信设计与实现

目录:

目录

目录:

一、背景及意义:

二、国内外研究现状

2.1 国外研究的现状

2.1.1  国际可见光通讯联盟

2.1.2 日本的研究进展

 2.1.3 德国的研究进展

2.1.4 英国的研究进展

 2.1.5 美国的研究进展

2.2 国外研究的现状

三、系统设计方案

3.1 系统概述

3.2 系统组件及功能

3.2.3 传输介质:

3.2.4 接收端:

3.2 系统框图描述(图3-1)

3.3 发射端设计方案

3.4 接收端设计方案 

3.5 主控端设计方案 

3.6 系统功能设计方案

3.7 源码:

1. main.c

2. usart.c

 3. key.c

4. sys.c

5. gpio.c

 6. stm32f1xx_it.c

四、硬件设计:

4.1 系统架构:

4.2 工作流程:

4.3 技术亮点:

4.4 51单片机选择

4.5 单片机

4.5.1 设计思路概要:

4.5.2 设计要点:

4.5.3 电路设计中的考虑因素:

4.6 系统供电电路

4.7 系统时钟电路 

4.8 复位电路 

4.9 蓝牙和串口控制电路

4.10 按键电路 

4.11 MP3 音乐播放模块

4.12 音频信号的放大和调理模块

4.12.1 发射端:

4.12.2 接收端:

4.13 光调制模块:

4.14 光接收模块

4.15 音频模块:

4.16 发射端原理图:

4.17 接收端原理图: 

4.18 主控端原理图 

五、音乐播放系统的软件设计:

5.1 开发平台

5.2 系统主程序设计

5.3 系统子程序设计:

5.3.1 按键检测子程序设计

5.3.2 串口通信模块子程序设计

5.3.3 蓝牙串口助手配置过程

六、系统装置测试:

6.1 实物介绍

6.2 对其中一套发射端和接收端进行装置测试 

6.2.1 发射端与接收端距离 3cm

6.2.2 发射端与接收端距离 35cm

6.2.3 发射端与接收端距离 150cm

6.3 对整体系统装置进行测试

6.3.1 将电源接口接上电源,给硬件上电

6.3.2 按键发送不同指令时系统装置的工作状态 

6.3.3 蓝牙发送不同指令时系统装置的工作状态 

七、结果分析 

详细资源:毕设&课设&项目&竞赛-基于STM32+RTOS音频光通信设计与实现(完整工程资料源码).zip资源-CSDN文库

资料文件夹:

源码文件夹:


一、背景及意义:

想象一下,你走进一个昏暗的博物馆,四周陈列着众多远古文物,在微弱的灯光照射下,它们显得格外神秘。当你走到一束光源前,你的手机突然自动显示出关于这件文物的详细信息。更进一步,当你佩戴上智能眼镜时,眼前的文物信息便可以随意检索,仿佛置身于一个充满知识的虚拟世界。这一切的奇妙体验,源于一种能够进行信息传输的光束,这就是可见光通信。

随着光通信技术的不断进步,可见光通信作为一种新兴的通信手段,正受到越来越多的关注。与传统的无线通信技术相比,可见光通信具有诸多优势,它不受电磁设备的干扰,能够有效确保数据的安全性与隐私保护。此外,它具备极大的数据传输能力,环保节能,部署方便,基站数量多,信号强度高,对人体也没有任何危害。因此,可见光通信技术已在智能交通、智能家居和无线接入等多个领域得到了广泛应用。

图1-1 可见光通信应用前景图

强度调制/直接检测(IM-DD)是一种在现代可见光通信系统中广泛应用的光调制方式,也是本文实现可见光通信的核心调制技术。该技术通过调节半导体光源(如LED)的电流或电压,将携带信息的信号调制到光载波的强度上,并通过直接检测光信号的变化来恢复原始数据。图1-2展示了光调制的原理图。

随着互联网流量的急剧增长和大数据时代的到来,对高速率、大容量的光通信系统的需求不断提升,推动了IM-DD技术的快速发展。与相干接收系统相比,基于IM-DD的系统成本更低、结构更简单,因此在短距离和成本敏感型应用中备受青睐。然而,IM-DD系统也面临诸如线性失真、非线性效应和信道带宽限制等技术挑战。如何克服这些问题,是推动光通信技术进一步发展的关键所在。

通过深入研究IM-DD调制技术,可以开发出更高效的调制和信号处理方法,从而提高系统的传输速率和抗干扰能力。IM-DD技术的进步还可能催生新型调制格式和光电器件,进一步拓展光通信技术的应用范围。因此,IM-DD技术的研究在提升通信系统性能、推动新技术创新以及促进产业发展等方面具有深远的意义。

图1-2 IM-DD 光调制原理图

LED可见光通信(VLC,Visible Light Communication)是一种新兴的光学无线通信技术,它通过利用可见光波段(人眼可以感知的光)来传输数据。VLC的工作原理是将信息数据转换成电信号,这些电信号驱动LED灯或激光器高速闪烁,通过特定的闪烁模式来表示二进制的1和0,从而实现数据传输。接收端使用光电探测器(如光电二极管)来检测这些高速的光信号变化,并将其解码为数据。

可见光通信的特点与优势:

1. 与传统无线通信(如Wi-Fi、蓝牙)不同,VLC不使用射频,因此不会受到电磁设备的干扰,也不会产生电磁辐射。这使得VLC在一些对电磁环境有严格要求的场合(如医院的ICU、敏感工厂环境等)具有很大的优势。

2. 由于可见光不能穿透墙壁,因此基于VLC的通信不会像Wi-Fi信号那样通过墙壁扩散。这意味着在同一个房间外的黑客无法截获通信信号,极大地提高了数据的安全性。

3. 可见光的频谱远远大于无线电频谱,因此VLC理论上可以实现比传统无线通信更高的数据传输速率。

4.  VLC可以直接利用现有的LED照明基础设施进行部署,每一盏LED灯都可以作为一个通信基站。这意味着不需要额外建设通信网络,节约了能源和资源。

5. LED照明已经广泛应用于日常生活和工作场景,VLC的实施可以通过升级现有的照明设备来实现,因此其部署相对简单。

虽然可见光通信技术有许多优点,但在实际应用中仍然面临一些挑战,例如大功率照明光源的调制带宽相对较窄,环境光引起的强噪声干扰,以及光链路对遮挡的敏感性等。然而,随着LED技术的持续进步和可见光技术研究的深入,未来这种技术将因其无电磁辐射和抗电磁干扰的独特优势,依托广泛应用的LED,成为无线通信领域的重要力量,实现万物通过光互联的愿景。

图 1-3 LED 可见光通信原理图

表1-1 可见光通信与其他通信方式对比表:

二、国内外研究现状

2.1 国外研究的现状


可见光通信是利用可见光光源(如LED)进行无线数据传输的一种新兴技术,具备高频带宽、不受电磁干扰、节能环保等优势。VLC技术在全球多个国家的研究机构和企业中得到了广泛关注和发展,尤其在美国、欧洲和日本等国家和地区,其技术处于世界前沿。IEEE于2003年启动了VLC标准的制定,并公布了第一批标准,这些标准涵盖了低速、中速和高速数据传输的三种物理层类型。

2.1.1  国际可见光通讯联盟

该联盟是推动VLC技术进步的核心组织,由美国、欧洲和日本等多个国家的相关机构和公司组成。VLC技术已被广泛应用于军事和民用领域,例如军事指挥中心和卫星导航系统。

2.1.2 日本的研究进展

NTT研究所与NEC公司:2006年,这些机构合作开发了一种利用照明灯光传输高速信息的“可见光通信”系统。该技术被日本政府视为下一代宽带网的重要组成部分,预计在5年内实现实用化。

学术界的探索:早在2000年,KEIO大学的Tanaka教授及其团队与SONY计算机科学研究所的Haruyama合作,提出了一种室内白光LED无线通信系统,该系统利用LED照明灯作为通信基站,采用强度调制直接检测(IM-DD)方式进行无线传输的仿真建模,研究了数据率、误码率和接收功率之间的相互关系。

 2.1.3 德国的研究进展

布莱梅国际大学:布莱梅大学的Afgani代表赫兹通信工程研究所柏林股份有限公司和弗劳恩霍夫电信研究所,在2006年对基于单LED强度调制OFDM的技术进行了深入探讨,表明OFDM技术在一米范围内可显著降低峰平比。

海因里希研究所:2008年,该研究所对100MHz的VLC宽带接入网的物理层基础问题进行了研究,并在白光LED可见光通信系统中实现了101Mb/s的传输速度。在2010年,与西门子公司合作,使得传输速度提升至500Gbps。

2.1.4 英国的研究进展

牛津大学:主要研究LED光源的窄带特性,通过多谐振均衡技术将光源的有效带宽提升至25MHz,使系统传输速度达到75Mb/s。

 2.1.5 美国的研究进展

智慧照明计划:2008年,美国政府启动了“智慧照明”计划,旨在促进无线设备与LED照明设备之间的数据传输,采用可见光束技术,进一步推动了VLC的发展。

尽管VLC技术在多个国家取得了重要进展,但由于LED照明部门参与不足,VLC的最终标准尚未确定。各国研究团队仍在不断探索如何在技术层面进一步提升通信速度、稳定性和可靠性,以确保VLC技术能够更广泛地应用于未来的通信网络中。

国外在可见光通信领域的研究已经取得了显著进展,各国在具体技术方向和应用领域各有侧重。未来,随着更多标准的确立和产业界的参与,VLC技术有望在更多领域中得到应用和推广。

2.2 国外研究的现状

中国在可见光通信(VLC)技术领域的探索相较于日本、欧洲及美国等先行者,确实起步较晚,因而在实际应用层面仍面临一定挑战。不过,国内各大科研机构,特别是高等院校的半导体研究所,正积极填补这一差距,展现出强劲的研究动力与创新能力。

复旦大学作为该领域的佼佼者,其研究聚焦于高速VLC系统的研发与测试。在过去几年间,他们不仅实现了数据传输速率的显著飞跃,从最初的875Mbps跃升至1.5Gbps,乃至后来基于RGB三色白光LED技术的3.25Gbps高峰值速度,还创新性地展示了通过单一LED灯为多台设备提供上网服务的可能性,彰显了其在高速VLC系统领域的领先地位。

清华大学则深入剖析了室内VLC系统的信道特性、容量潜力及LED光源的优化利用,同时,他们在OFDM调制技术、室内定位系统及电力线与VLC融合通信方案上的探索,为VLC技术的多元化应用开辟了新路径。

北京邮电大学的研究团队则专注于VLC信道建模与LED驱动电路的均衡技术创新,其提出的光子追踪仿真算法,在提升系统效率与简化复杂度方面取得了显著成效,为VLC系统性能评估与优化设计提供了有力工具。

南京邮电大学则在MIMO(多输入多输出)VLC系统领域取得了突破性进展,其多用户MIMO系统在高通信速率下展现出的极低误码率,为高密度通信场景下的高质量数据传输提供了可能。

东南大学的研究则更为广泛,覆盖了多用户接入、信道容量分析、光源布局优化、OFDM调制与信道均衡等多个方面,通过大量仿真研究与改进方案的提出,为VLC技术的全面发展贡献了重要智慧。

进入2022年,中国移动携手中关村泛联移动通信技术创新应用研究院及复旦大学,共同推出了面向6G网络的超高速VLC器件与样机。这一成果不仅突破了传统VLC技术在器件带宽与通信距离上的限制,还通过氮化镓蓝光超辐射发光二极管(GaN SLD)芯片的创新应用,实现了单管4.57Gbps的国际领先传输速度,并支持1080P高清视频的远距离即时传输。这一系列技术突破,不仅为6G通信网络的构建提供了新型频谱资源支持,更为VLC技术的未来发展奠定了坚实基础,预示着光通信领域即将迎来新的变革与飞跃。

三、系统设计方案

3.1 系统概述

该系统旨在通过光调制技术实现分区音乐播放,允许用户通过主控端对多个区域(通过发射端和接收端划分)独立控制音乐播放,包括曲目选择、音量调节和播放控制。

3.2 系统组件及功能

3.2.1 主控端

  • 主控制器:作为系统的控制中心,负责接收用户输入(如通过触摸屏、遥控器或手机APP等),并通过串口通信向发射端发送控制命令。
  • 用户界面:提供用户交互界面,允许用户选择曲目、调整音量和进行播放控制。
  • 串口通信模块:负责将主控端的控制命令以数字信号形式发送给发射端。

3.2.2 主控端

  • 核心控制器:接收来自主控端的命令,解析并控制各个模块执行相应的操作。
  • 光调制模块:将音频信号(经过数字化处理)转换为光信号。这通常涉及将音频信号调制到光波上,例如通过改变LED灯的亮度或频率来编码音频信息。
  • LED光源:作为光信号的发射源,根据调制信号的变化发出可见光。
  • 电源管理:为发射端各模块提供稳定电源。

3.2.3 传输介质

  • 可见光:作为音频信号的传输媒介,通过空气或特定介质(如光纤,但在此场景下通常使用直接可见光)传输调制后的光信号。

3.2.4 接收端

  • 光接收模块:接收来自发射端的光信号,并将其转换为电信号。
  • 解调模块:将接收到的光信号中的音频信息解调出来,还原为原始的音频信号。
  • 音频处理模块:对解调后的音频信号进行必要的处理(如放大、滤波等),以便驱动扬声器。
  • 扬声器:将处理后的音频信号转换为声音,实现音乐播放。
  • 电源管理:为接收端各模块提供稳定电源。

3.2 系统框图描述(图3-1)

  • 顶部(主控端):一个矩形框表示主控制器,下方连接一个用户界面框,旁边是一个串口通信模块框,箭头指向下方,表示数据流向发射端。
  • 中部(发射端):两个并列的矩形框,每个代表一个发射端。每个框内包含核心控制器、光调制模块和LED光源的图标,核心控制器接收来自主控端的箭头,并控制光调制模块和LED光源。
  • 下部(传输介质):使用波浪线或光线路径表示从发射端到接收端的可见光传输。
  • 底部(接收端):与发射端对应,也是两个并列的矩形框,每个框内包含光接收模块、解调模块、音频处理模块和扬声器的图标。光接收模块接收来自上方的光信号,并依次通过解调模块、音频处理模块,最终由扬声器输出声音。

图 3-1 总体设计框图 

这个系统框图简洁地展示了基于光调制的分区音乐播放系统的各个组成部分及其之间的数据流和控制流。

3.3 发射端设计方案

在发射端的设计中,我们采用了STM32F103C8T6作为核心控制器(MCU)的核心板,该控制器负责接收来自主控端的指令,并对这些指令进行解析和执行相应的操作。发射端的系统不仅限于简单的指令处理,还集成了丰富的功能模块以支持音乐播放和光调制传输。

图 3-2 发射端设计框图

  1. 按键输入模块:包含9个按键,用于本地操作或调试,如选择曲目、调整音量等。这些按键信号被STM32单片机读取并处理。

  2. MP3音乐播放模块:该模块负责存储和播放音乐文件。当STM32单片机接收到主控端的音乐播放指令(包括选择特定曲目)时,它会通过I2C、SPI或其他串行通信接口与MP3模块通信,控制其播放相应的音乐。

  3. 音频信号放大与调理模块:由于MP3模块输出的音频信号可能较弱或需要特定的格式调整,该模块用于对音频信号进行放大、滤波、均衡等处理,以确保音频信号的质量适合后续的光调制和传输。

  4. 光调制模块:该模块是发射端的核心之一,它负责将经过放大和调理的音频信号转换为光信号。这一过程通常涉及到将音频信号的电压或电流变化映射为光强或光频率的变化,以便通过可见光进行传输。光调制模块可能包含LED驱动器、LED光源(如RGB LED)和相关的控制电路。

在接收系统中,同样采用STM32单片机作为控制核心。当光调制模块发出的光信号被接收端的光接收模块捕获并转换为电信号后,该电信号首先经过解调处理,还原为原始的音频信号。随后,该音频信号经过必要的放大和调理(如果接收端也包含这些模块的话,虽然有时这些处理可能在音频解码或播放模块内部完成),最终由MP3模块或其他音频播放设备输出为可听的声音。

3.4 接收端设计方案 

接收模块中接收到光信号后将光信号转换为电信号,经音频信号的放大和调理模块处理后传给音频播放模块。最终可以播放音质较好的音乐。接收端系统框图如图3-3所示。

图 3-3 接收端设计框图 

3.5 主控端设计方案 

系统使用型号为STC8H8K64U的升级版51单片机作为主控制单元,利用手机上的蓝牙串口助手软件发送指令。当控制单元接收到指令后,会通过串口向两个STM32核心控制器传递相应的命令。这些命令包括选择音轨、调节音量、播放和停止等功能。

通过蓝牙模块,手机可以向51单片机发送指令,从而实现对两个STM32单片机的同时或独立控制。这种配置支持分区控制,使得两套接收端能够分别进行音乐播放,进而实现分区音乐播放的功能。接收端的系统框图如图3-4所示。

图 3-4 主控端设计框图 

3.6 系统功能设计方案

在完成硬件设计并连接后,对软件进行配置参数、编译调试代码并进行功能检测。另外,设计调整发射与接收系统电路,使播放音乐的音质达到最佳。最后,发射端与接收端,能成功在空气中进行信号传递,实现可见光通信,并且期望尽可能加长传输距离、加快传输速率、降低系统能耗。

此外,配置手机蓝牙串口助手控制,使手机能成功发送切换、暂停歌曲加减 音量指令给 51 单片机实现分区播放音乐的功能。基于光调制的分区音乐播放系 统的设计方案。通过控制系统发控制信号,控制两个声道同时播放、分别播放音 乐、加减音量暂停等。实现分区播放音乐功能。通过发送系统由 LED 灯将光调 制的音频信号发送到空气中,通过调整发送系统 LED 灯与光敏三极管之间的距 离为 20~40 厘米时,接收系统预期能够将接收的信号由声道进行播放且音质较 好,实现了光调制音乐播放的功能。

3.7 源码:

1. 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 "sys.h"
#include "key.h"
#include "delay.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 */
const u8 mp3_cmd_default_volume[8] = {0x7E, 0xFF, 0x06, 0x06, 0x00, 0x00, 0x0A, 0xEF};
/* 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 */

/* 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 */
    delay_init(72);               		//初始化延时函数 
    delay_ms(500);
    HAL_UART_Transmit(&huart1, mp3_cmd_default_volume, 8, 300);
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
      Key_Proc();
  }
  /* 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 */

/* 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)
  {
  }
  /* 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 */

2. usart.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.c
  * @brief   This file provides code for the configuration
  *          of the USART instances.
  ******************************************************************************
  * @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 "usart.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

UART_HandleTypeDef huart1;

/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 9600;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

 3. key.c

#include "main.h"
#include "key.h"
#include "delay.h"
#include "string.h"
#include "usart.h"

const u8 mp3_cmd_stop[8] = {0x7E, 0xFF, 0x06, 0x16, 0x00, 0x00, 0x00, 0xEF};
const u8 mp3_cmd_volume_up[8] = {0x7E, 0xFF, 0x06, 0x04, 0x00, 0x00, 0x00, 0xEF};
const u8 mp3_cmd_volume_down[8] = {0x7E, 0xFF, 0x06, 0x05, 0x00, 0x00, 0x00, 0xEF};
const u8 mp3_cmd_music01001[8] = {0x7E, 0xFF, 0x06, 0x0F, 0x00, 0x01, 0x01, 0xEF};
const u8 mp3_cmd_music01002[8] = {0x7E, 0xFF, 0x06, 0x0F, 0x00, 0x01, 0x02, 0xEF};
const u8 mp3_cmd_music01003[8] = {0x7E, 0xFF, 0x06, 0x0F, 0x00, 0x01, 0x03, 0xEF};
const u8 mp3_cmd_music02001[8] = {0x7E, 0xFF, 0x06, 0x0F, 0x00, 0x02, 0x01, 0xEF};
const u8 mp3_cmd_music02002[8] = {0x7E, 0xFF, 0x06, 0x0F, 0x00, 0x02, 0x02, 0xEF};
const u8 mp3_cmd_music02003[8] = {0x7E, 0xFF, 0x06, 0x0F, 0x00, 0x02, 0x03, 0xEF};

void Key_Proc(void)
{
    //01001
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_music01001, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET)break;
                }
            }
        }
    }
    
    //01002
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_music01002, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET)break;
                }
            }
        }
    }
    
    //01003
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_music01003, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_SET)break;
                }
            }
        }
    }
    
    //02001
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_music02001, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_SET)break;
                }
            }
        }
    }
    
    //02002
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_music02002, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_SET)break;
                }
            }
        }
    }
    
    //02003
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_music02003, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_SET)break;
                }
            }
        }
    }
    //音量增加
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_volume_up, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET)break;
                }
            }
        }
    }
    //音量减小
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_volume_down, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET)break;
                }
            }
        }
    }
    //停止
    if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET){//按下检测
        delay_ms(10);
        if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET){//确认按下
            HAL_UART_Transmit(&huart1, mp3_cmd_stop, 8, 300);
            delay_ms(10);
            while(1){//释放检测
                if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_SET){
                    delay_ms(10);
                    if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_SET)break;
                }
            }
        }
    }
}



4. sys.c

#include "sys.h"

//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK Mini STM32开发板
//系统中断分组设置化		   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/10
//版本:V1.4
//版权所有,盗版必究。
//Copyright(C) 正点原子 2009-2019
//All rights reserved
//********************************************************************************  

//时钟系统配置函数
//PLL:选择的倍频数,RCC_PLL_MUL2~RCC_PLL_MUL16
//返回值:0,成功;1,失败
void Stm32_Clock_Init(u32 PLL)
{
    HAL_StatusTypeDef ret = HAL_OK;
    RCC_OscInitTypeDef RCC_OscInitStructure; 
    RCC_ClkInitTypeDef RCC_ClkInitStructure;
    
    RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE;    	//时钟源为HSE
    RCC_OscInitStructure.HSEState=RCC_HSE_ON;                      	//打开HSE
	RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1;		//HSE预分频
    RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;					//打开PLL
    RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE;			//PLL时钟源选择HSE
    RCC_OscInitStructure.PLL.PLLMUL=PLL; 							//主PLL倍频因子
    ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);//初始化
	
    if(ret!=HAL_OK) while(1);
    
    //选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
    RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
    RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;		//设置系统时钟时钟源为PLL
    RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;				//AHB分频系数为1
    RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2; 				//APB1分频系数为2
    RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1; 				//APB2分频系数为1
    ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2);	//同时设置FLASH延时周期为2WS,也就是3个CPU周期。
		
    if(ret!=HAL_OK) while(1);
}

#ifdef  USE_FULL_ASSERT
//当编译提示出错的时候此函数用来报告错误的文件和所在行
//file:指向源文件
//line:指向在文件中的行数
void assert_failed(uint8_t* file, uint32_t line)
{ 
	while (1)
	{
	}
}
#endif
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI  
void WFI_SET(void)
{
	__ASM volatile("wfi");		  
}
//关闭所有中断
void INTX_DISABLE(void)
{		  
	__ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
	__ASM volatile("cpsie i");		  
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0 			//set Main Stack value
    BX r14
}

5. gpio.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.c
  * @brief   This file provides code for the configuration
  *          of all used GPIO pins.
  ******************************************************************************
  * @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 "gpio.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/** Configure pins as
        * Analog
        * Input
        * Output
        * EVENT_OUT
        * EXTI
*/
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pins : PA0 PA1 PA2 PA3
                           PA4 PA5 PA6 PA7 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                          |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pin : PB0 */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

 6. stm32f1xx_it.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f1xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @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 "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

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

/* USER CODE END TD */

/* 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 */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M3 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Prefetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVCall_IRQn 0 */

  /* USER CODE END SVCall_IRQn 0 */
  /* USER CODE BEGIN SVCall_IRQn 1 */

  /* USER CODE END SVCall_IRQn 1 */
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/**
  * @brief This function handles Pendable request for system service.
  */
void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f1xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

四、硬件设计:

4.1 系统架构

  • 控制端:使用STC8H8K64U51单片机作为主控制器,通过蓝牙接收来自手机的指令。这些指令用于控制音乐播放器的各种功能,如选择曲目、调整音量、播放和停止等。
  • 发射端:每个发射端使用STM32F103C8T6的MCU核心板来解析并执行来自控制端的指令。其上设置了包括程序下载接口、复位电路、晶振电路等,保证系统的基本功能和扩展能力。
  • 接收端:采用纯模拟电路设计,将接收到的光强度信号转化为音频信号,经过滤波和放大处理后,通过音频功放和喇叭输出声音。

4.2 工作流程

  • 用户通过手机蓝牙发送指令给控制端。
  • 控制端的主控制器解析这些指令并通过串口通信将指令发给两个发射端。
  • 发射端的核心控制器(STM32F103C8T6)接收并解析指令,然后执行相应的操作,这可能包括激活某个光源或调整光调制方式。
  • 接收端检测光强度变化,将其转化为对应的音频信号并经过放大后输出。

4.3 技术亮点

  • 使用蓝牙进行无线控制,便于用户操作。
  • 结合数字和模拟电路设计,充分发挥各自优势。
  • 采用STM32F103C8T6提供较强的计算能力和丰富的外设接口,支持复杂的信号处理和控制逻辑。

4.4 51单片机选择

从架构角度分析:51单片机采用的是8位RISC架构,指令集简单直观,寻址模式有限。而STM32则基于32位RISC-V架构,具备更复杂的指令集和丰富的寻址模式,支持更高的数据处理速度和精度。

从性能和资源来看:51单片机的性能较低,主频通常不超过40MHz,处理能力有限;而STM32的主频通常超过100MHz,处理能力更强,适合处理复杂的应用场景。此外,STM32在内存资源上也优于51单片机,拥有更大的Flash和SRAM存储空间,并支持更丰富的外设接口。

在开发环境方面:51单片机的传统开发环境是Keil C51,而STM32则支持多种开发工具,例如Keil MDK、IAR和TrueSTUDIO,同时还拥有更先进的调试接口,如JTAG和SWD。

从需求出发,虽然该系统的主控模块不需要高性能,考虑到成本,51单片机是一个合适的选择。不过,相较于传统51单片机,STC8H8K64U提供了更多的接口,例如多个串口、更强大的定时器和计数器,使其具备处理复杂应用的能力。

对于更高性能要求的应用,STM32无疑是核心控制器的最佳选择。它具备出色的性能和稳定性。虽然STM32的F0系列属于入门级,F2、F3、F4、F7、H7系列过于高端,但F1系列则非常适合用于光调制分区音乐播放器。因此,最终决定选用STM32F103C8T6作为核心控制器,以控制系统的各项功能。

4.5 单片机

在设计中使用了两种不同类型的单片机——STC8H8K64U(一种升级版的8位51单片机)和STM32F103C8T6(基于32位ARM Cortex-M3内核的单片机)。这两款单片机具有各自的优点,并且通过串口实现了彼此之间的通信。

4.5.1 设计思路概要:

  1. STC8H8K64U 主要负责指令的发送端,利用其高速运算能力和丰富的外围接口(如多种串口、定时器等)进行控制逻辑的执行。
  2. STM32F103C8T6 通过串口1接收来自STC单片机的命令,并利用其高主频和强大的计算能力进行数据处理,使用其丰富的外设(如ADC、SPI、UART等)来执行复杂的任务。

4.5.2 设计要点:

  • 通信接口设计:您选择了STC8H8K64U的串口2(TX端)和STM32F103C8T6的串口1(RX端)相连,确保了两个单片机之间的数据通信稳定和高效。通过串口通信可以实现命令的分发与接收,从而实现系统的控制逻辑。
  • 电路原理图:您提到了设计的电路原理图,可以通过此图展示这两个单片机之间的串口连接、供电情况以及其他外围设备的连接。若您有具体的电路图示,建议标明每个引脚的具体连接关系及用途。

4.5.3 电路设计中的考虑因素:

  1. 供电电压匹配:STC8H8K64U工作电压为4.2V~5.5V,而STM32F103C8T6通常工作在3.3V,因此在实际电路设计中,可能需要使用电平转换电路(如电平转换芯片或电阻分压电路)来匹配两者之间的信号电平。
  2. 串口通信的波特率匹配:确保STC8H8K64U和STM32F103C8T6的串口波特率一致,以确保数据传输的可靠性。
  3. 去耦电容:为确保电路的稳定运行,特别是在频率较高的STM32单片机中,建议在供电引脚附近布置去耦电容,以滤除电源噪声。
  4. 干扰抑制和抗噪设计:尤其是针对STC8H8K64U的抗干扰能力,可以考虑增加电磁屏蔽、合适的滤波器等,确保系统的稳定运行。

 

图 4-1 控制器电路连接原理 

4.6 系统供电电路

本设计中,51单片机和STM32最小系统均支持3.3V供电电压,STM32核心板自带3.3V稳压器。然而,存在两个问题需要解决:其一,5V电压等级的USB接口输出电流有限,最大输出电流仅为500mA,无法满足其他器件的电压需求以及整个系统的电流要求;其二,核心板内置的5V转3.3V稳压器由于封装较小,散热性能较差,导致其无法提供较大的电流输出。因此,设计中采用了外部电源来提供系统供电。

此外,考虑到本设计中部分芯片需要6V以上的供电电压(后文会详细说明),为增强系统的设计灵活性与兼容性,采用了9V电源适配器作为较高电压输入。同时,通过线性稳压器来生成稳定的5V和3.3V电压等级。多种电压等级的设计不仅能分散各个稳压器的散热压力,还为后续的设计提供了多种可用的电压选择。

具体的工作原理如下:外部电源适配器接入后,首先经过100uF和100nF的滤波电容组合进行滤波处理,随后电压输入到7805稳压IC,再通过输出端的滤波电容,生成稳定的5V电压。接着,经过3.3V稳压器SPX1117进一步稳压,输出稳定的3.3V电压。稳压电路如图4-2所示。

图 4-2 稳压电路 

4.7 系统时钟电路 

针对 STC8H8K64U 单片机,其内部集成了高精度的 RC 振荡器,能够满足大多数应用场景下的时钟需求,从而省略外部时钟电路,降低成本、简化设计并提升系统的可靠性。

在本设计中,STM32 核心板采用了 STM32 单片机常见的晶振配置,即组合使用高频和低频晶振。由于 STM32 内部振荡器的精度有限,因此本设计引入了一个高频晶振作为系统的主时钟,以满足对时间精度要求较高的应用。此外,还配置了一个 32.768kHz 的低频晶振,主要用于低频定时控制、低功耗模式以及 RTC 模块的时间计量需求。如图 4-3 所示,为系统时钟电路的原理图。

图 4-3 系统时钟

4.8 复位电路 

STC8H8K64U单片机内置了专用的MAX810复位芯片,因此在上电时无需外部复位电路即可实现可靠的复位功能。

相比之下,STM32单片机支持三种复位模式:系统复位、上电复位和备份区域复位。在电路设计时,通常只需关注上电复位。此外,还可以通过按键实现手动复位功能。

STM32的外部复位引脚NRST为低电平有效状态,这意味着当该引脚为低电平时,系统会保持在复位状态;而当电压升高后,系统将从引导区域开始执行程序。为提高灵活性,本设计中加入了RC上电复位电路和按键复位电路,以便在需要时手动复位系统。复位电路的示意图如图4-4所示。

图 4-4 复位电路 

4.9 蓝牙和串口控制电路

在控制端中,使用的蓝牙模块是采用串口通信的,因此需要在单片机中至少具备一个串口接口以满足设计需求。其中,串口的接收端(RX)用于接收蓝牙指令,而发送端(TX)用于向发射端分发指令。

在具体应用中,选用了汇承公司的HC-02串口透传蓝牙模块。这是一种基于Bluetooth Specification V2.0并支持EDR(Enhanced Data Rate)协议的数据传输模块。该模块能够在2.4GHz ISM无线频段工作,采用高斯频移键控(GFSK)调制方式,具备4dBm的最大发射功率和-85dBm的接收灵敏度,能够支持10米的通信距离。此外,该模块支持透传模式,可以将串口数据直接传输到蓝牙设备上,从而实现无线串口传输功能,使用非常简便。图4-5展示了蓝牙模块的接口电路图。

图 4-5 蓝牙模块接口电路 

4.10 按键电路 

本设计旨在实现两个发射板内部音乐模块之间的切换功能,这两个模块各包含一个文件夹,内含六首音乐。此外,系统还需具备音量调节、播放/暂停等与 MP3 播放器类似的功能。主要的控制方式是通过蓝牙接收器与串口进行控制。同时,为了提升系统的可靠性,设计中额外加入了按键电路,以备在控制端发生故障时可通过按键进行控制。

为了实现上述控制功能,若使用独立按键,则至少需要9个按键;而若采用矩阵按键,便可选择使用3x3或4x4的矩阵形式。鉴于 STM32 单片机的 I/O 口资源丰富,本设计选择了独立按键方案,因其电路结构更为简单,程序编写也较为直接。

独立按键的工作原理为:当按键被按下时,电平会被拉低,单片机便能检测到该端口电平为0,从而执行相应的功能程序。按键电路的具体电路图可见于图4-6。

图 4-6 按键电路

4.11 MP3 音乐播放模块

模块简介:MP3-FLASH-16P 是一款串口控制的语音模块,具备完整的 MP3 和 WAV 音频文件硬件解码功能。它不仅支持 MP3 和 WAV 格式的解码,还兼容工业级串口通信协议,并使用 SPI FLASH 作为存储介质。用户可以根据需求选择合适的 SPI FLASH 设备来存储音频文件。通过简单的串口指令,用户可以轻松播放特定的音频文件,无需复杂的底层操作。这款产品以使用简便、稳定可靠而著称。MP3-FLASH-16P 模块的实物和原理图可以参见图 4-7。

图 4-7 MP3-FLASH-16P 模块实物及原理 

这款 MP3 模块不仅配备了高性能的硬件解码芯片,还内置了一个16位微控制器,大大提高了其灵活性。解码芯片的使用确保了模块的稳定性和音质。此外,为了提升易用性,该模块采用了高度集成的封装设计,在较小的体积内实现了所有功能。硬件结构如图4-8所示。 

在该电路设计中,我们使用了 MP3-FLASH-16P 模块来存储和输出音频信号。该模块支持两种控制方式:串口控制和按键控制。由于按键控制在灵活性方面存在一定的限制,因此我们选择了串口控制。具体来说,MP3 音乐模块的 RX1 和 TX1 引脚连接到 STM32 微控制器的 PA10 和 PA9 引脚,进行 UART 串行通信。这种设置允许我们对模块进行各种控制操作。DAC-R 引脚则输出标准的音频信号,系统将这个音频信号用作后续信号发射部分的音频源。 

4.12 音频信号的放大和调理模块

模块介绍:LM358是一种通用集成运算放大器,内部包含两个高增益、宽频率补偿的运放,可在广泛的电压范围内支持单电源或双电源操作。其内部电路设计确保了电流和电压的独立性。该运放适用于传感器放大器、直流增益模块以及任何需要单电源供电的运算放大器应用场合。

电路设计:本设计利用LM358双运算放大器及其外围电路,实现对音频信号的放大和调理,适用于发射端和接收端。在这两个端口,需要分别对不同类型的音频信号进行处理,以实现音频信号的有效传输和转换。

4.12.1 发射端:

图 4-9 发射端信号放大调理模块电 

在本电路中,双运算放大器LM358的反相输入端连接到音乐播放模块MP3-FLASH-16P的DAC-R引脚,结合加法运算电路和电容耦合电路,以实现对音频信号的放大和调理。LM358的输出端则连接到后级LED恒流驱动器PT4115的DIM引脚,用于进行开关使能、模拟调光与PWM调光。

前级MP3模块输出的音频信号为交流信号,幅值较小,而后级LED恒流驱动芯片需要一个包含1.5V直流分量的交流信号,同时为了确保动态性能,要求信号具有更大的幅值。因此,运算放大器的任务是将前级输出的信号调整为符合后级输入要求的信号。

在运放电路中,前段的电容C19作为隔直电容,利用电容对直流信号的阻隔特性,将信号中叠加的直流成分隔离开,确保只有有效的音频交流信号能够通过运放进入后续处理部分。

运算放大器的工作主要可以分为两个部分:一方面是信号放大。图3-9展示的是一个差分放大电路。如果假设该放大电路使用理想的运算放大器,由于其开环增益为无限大,因此运算放大器的两个输入端可以视为虚接地。同时,由于输入阻抗无限大,从输入端Vin到反相输入端V-的电流与从反相输入端V-到输出端Vout的电流相等,这也就得出以下公式:

在发射端设计中,我们采用反相闭环放大器电路来实现信号的固定放大。通过使用固定电阻 R3(2kΩ)和 R10(100kΩ)的组合,能够将输入信号放大 50 倍。

另外,为了实现电平移位功能,我们设计了一个特别的电路。在常规反相放大电路中,如果输入和输出是纯交流信号,正输入端通常接地。但是在本设计中,我们需要一个带有直流偏置的交流信号。因为发射端使用了 PT4115 恒流芯片,该芯片要求直流偏置为 1.5V。

为此,我们利用运算放大器作为差分放大电路的原理来实现这一目标。该电路的基本结构就像图 4-10 所示。通过这种设计,能够在输出端叠加所需的直流偏置,从而满足 PT4115 芯片的要求。电路中的 V+计算可通过差分放大电路的公式来得出。

 图 4-10 运放组成的差分放大电路基本结构

可以这样表述:当U2是没有直流偏置的交流信号时,该电路的输出等于将这个交流信号放大到一定倍数后,再叠加上正输入端的直流电压。因此,该电路可以称为交流信号的电平移位电路。这个电路的一个显著优点是,电平移位和信号放大的功能是独立的,互不干扰。这意味着通过在运算放大器的正输入端引入直流偏置,可以实现电平移位,而此时对于信号的发射端,偏置电压约为1.67V。 

4.12.2 接收端:

图 4-11 发射接收端信号放大调理模块电路图 

电路是一个用于音频信号处理的电路,主要利用运算放大器(如LM358)对接收到的信号进行放大和调理。具体来说,电路主要完成以下几个任务:

1. 信号放大功能

  • 由于前级光敏三极管输出的音频信号幅值较小,需要通过运算放大器进行放大。反相输入端接光敏三极管集电极,形成闭环放大电路。通过电位器调节放大倍数,可以灵活适应不同的输入信号幅度,确保输出音频信号足够大且稳定,便于后续功放模块(如TDA2030A)进行功率放大。

2. 直流隔离

  • 由于接收端的光敏三极管输出信号中可能含有直流分量,而音频功放需要纯净的交流信号,因此在运算放大器前级加入了隔直电容。该电容用于去除直流分量,仅让交流音频信号通过,从而避免直流分量影响后级的音频放大质量。

3. 电平移位功能

  • 信号的输入和处理需要电平移位。为了防止信号放大过程中出现顶部或底部失真,运算放大器的正输入端引入了直流偏置电压,通常选取为电源电压的一半(如4.5V),使得信号在放大时能够充分利用电源电压的全范围,而不受供电电压上下限的约束。

4. 耦合和电平匹配

  • 由于发射端和接收端的电路环境不同,信号处理需要进行电容耦合以及电平移位,以适应不同的电路特性。通过这种方式,确保了信号可以在各级之间稳定传输,同时匹配各级电路的输入和输出特性,避免信号失真或幅值过小的情况发生。

这个电路通过一系列的信号调理(如信号放大、直流隔离、电平移位等),确保从光敏三极管接收到的音频信号能够被后级功放模块正确处理,输出稳定且放大后的音频信号。

4.13 光调制模块:

在这个设计中,我们使用光作为通信介质,所以需要为发射端设计一个LED灯驱动模块。由于LED灯的发光强度与其工作电流之间存在线性关系,因此我们需要利用与电压信号强度成线性关系的电流信号来进行驱动。在发射端,我们采用PT4115恒流源模块,将电压形式的音频信号转换为电流信号,以此驱动LED发射光信号,从而实现光调制。

图3-12 光信号发射和接收模块电路图 

PT4115是一种专为驱动高功率LED设计的降压恒流源芯片,适用于1W或3W的串联LED。它提供了两种封装选择:SOT89-5和ESOP8,输出电流可调,最高达到1.2A。通过调节输入电压及外部组件,PT4115可以驱动多颗高功率LED,总功率可达几十瓦。

PT4115内部集成电源开关,利用高压电流采样来设定LED的平均电流。它具备DIM引脚,支持模拟和宽范围PWM调光。在输出电压低于0.3V时,电源开关会断开,使得PT4115进入低功耗的备用模式。

在光信号发射模块中,使用1W白色LED作为光源。在开放环境中,该配置可支持约50厘米的光信号传输距离。若结合适当的光路设计,传输距离可以延长到几米。这意味着,在适当的条件下,PT4115可以有效地用于短距离的光通信应用。

4.14 光接收模块

模块介绍:本设计中用到的通信介质为光,因此需要设计接收端的光敏接收 模块。接收端使用光敏三极管,型号为 PT928-6C 来完成光信号的接收,并且通 过外围电路的合理设计,将接收到的光信号转变为电压信号,并提供给后级的信 号调理电路。发射和接收模块电路图如下图 3-8:

图 4-13 光信号发射和接收模块电路图 

光信号发射和接收模块的电路设计中,光敏元件的选择至关重要。常见的光敏元件包括光敏电阻、光敏二极管和光敏三极管。由于音频信号的频率可以达到几十千赫兹,光敏电阻的响应速度较慢,可能导致信号产生畸变,因此在此应用中选择光敏三极管作为接收端。

光敏二极管和光敏三极管都是光电转换的半导体器件,较光敏电阻具备更高的灵敏度、更好的高频性能、可靠性以及更小的体积和更便捷的使用。然而,光敏二极管的输出信号幅度较低,需要通过跨阻放大器(如TDA)进行信号放大,导致电路结构复杂并且成本较高,从而不适合大规模商业推广。因此,内置放大电路的光敏三极管成为更合适的选择,用于完成光信号到电信号的转换。

光电三极管,或称光敏三极管,是一种依赖外部光照控制电流的半导体光电器件。其结构类似于在三极管的基极与集电极之间连接一只光电二极管,光电二极管的电流相当于三极管的基极电流。由于光电三极管具有电流放大的特性,相较于光电二极管,其灵敏度显著提升,使得在集电极处能够输出较大的光电流。在无光照射的情况下,光电三极管处于截止状态,不会输出电信号。一旦光敏三极管的敏感部分接收到外界光信号,其集电极将按照一定的比例系数产生相应的电流。光敏三极管的敏感部分实际上包括了一个光电二极管,而内置的放大部分则进一步放大光电流,并在外部输出放大的电流信号。这种电流信号可以从集电极或发射极引出,具有较大的灵活性。 

4.15 音频模块:

模块介绍:TDA2030A 是由德律风根生产的音频功率放大器集成电路。它采用了 V 型 5 脚单列直插的塑料封装方式,基于引脚的形态可以分为 H 型和 V 型。这种集成电路在汽车音响系统和中功率音频设备中应用广泛,因其体积小、输出功率高和失真率低的优点而受到青睐。此外,它还具备内置保护电路。在本次设计中,我们使用了以 TDA2030A 音频功放 IC 为核心的功放模块来驱动扬声器发声,具体功放模块的实物如图 4-14 所示。

图 4-14 TDA2030A 功放模块实 

在电路设计中,本项目的目标是将发送端的音频信号通过接收端的音频模块转变为可听的声音。因此,在接收端需要配备具有一定功率的扬声器和相应的功率放大模块。为了实现功率放大,使用了基于TDA2030A芯片的现成功放模块。这个模块充分发挥了TDA2030A的性能,能够提供最大18W的输出功率。由于本设计主要是为了验证音频光通信这一核心技术,并不涉及如家庭影院等高端需求,因此选择了8欧姆、3W的扬声器来进行声音播放,音量已足够响亮。使用TDA2030A模块时,它提供了必要的接口,以便搭建如图3-15所示的放音电路。

图 4-15 TDA2030A 模块为核心的放音电路 

功率放大器(简称功放)是一种关键电子设备,其主要作用是将音频信号的电压和电流进行综合放大,进而提升信号的整体功率输出。功率放大器通常位于扬声器系统的前端,其输出直接连接到扬声器系统。在驱动扬声器时,功率放大器的输出灵敏度一般约为0分贝,因此输入信号往往来自调音台或其外围设备。然而,在本设计中,由运算放大器LM358提供的前置放大输出信号虽然电压幅值足够,但其电流驱动能力较弱,因此不能有效推动几瓦功率的喇叭工作。这就需要使用功率放大器来增强输出信号的电流和功率,显著提升负载能力,使扬声器能够正常工作并产生所需的声音。

4.16 发射端原理图:

4.17 接收端原理图: 

4.18 主控端原理图 

五、音乐播放系统的软件设计:

5.1 开发平台

在单片机控制系统开发过程中,需要编写程序来对单片机进行控制,因此选择合适的编译软件至关重要。作为一种应用广泛的编程语言,C 语言得到了大量开发者的青睐,本文也采用 C 语言来实现对单片机的控制功能。

为了使编写的程序能够被单片机识别和执行,必须经过特定的转换过程,将程序转换为机器可读的代码。这个过程通常由编译器完成。对于嵌入式系统开发,开发者常使用集成了编译器的开发环境(IDE)来创建项目、编写代码、进行编译和调试等。这类软件具有强大的功能和高度的集成性,被统称为集成开发环境(IDE)。

在本次光调制分区音乐播放系统的开发中,使用了 Keil 5 MDK 和 STM32 CubeMX 这两款开发工具。Keil 5 MDK 是一个集成开发环境(IDE),用于微控制器的编程开发。它包含了源代码编辑器、项目管理器、调试器以及用于微控制器开发的各种工具。而 STM32 CubeMX 是由意法半导体(STMicroelectronics)开发的一款辅助软件,专为 STM32 单片机设计。该工具通过图形化配置,可以根据硬件需求快速生成初始化代码,从而降低开发人员的开发难度,节省时间和成本。

Keil 5 MDK 的主界面如图 5-1 所示。

 图 5-1 Keil 5 MDK 程序设计

5.2 系统主程序设计

程序设计是一门逻辑思维的艺术,它是实现逻辑功能运行的基础。在嵌入式开发中,程序设计与单片机的特性紧密结合,经常涉及到对各种硬件模块的操作。在这种复杂的软件开发过程中,理清思路、确定程序设计的逻辑和运行顺序是至关重要的。否则,很容易在后续设计中出现不可预料的错误,也就是我们通常所说的BUG。根据设计需求,我们可以绘制出如图5-2所示的主程序流程图。

图 5-2 主程序流程图 

首先,系统需要进行一系列初始化步骤,这包括微控制器的内部时钟设置、IO端口配置、参数设定、外设模块初始化以及变量初始化等。完成这些准备工作后,系统进入待机状态,等待用户按键操作。根据用户的按键输入,系统通过串口与MP3模块通信,发送相应的指令以执行特定功能,例如播放选定的音乐、调整音量大小、开始或停止播放等。为了确保系统能够连续运行并持续监测按键操作,程序采用了死循环的方式进行控制,直到系统断电为止。

5.3 系统子程序设计:

5.3.1 按键检测子程序设计

图 5-3 按键检测模块流程 

 

机械式弹性开关的按键抖动问题是电子设计中常见的挑战之一。按键抖动是由于机械开关在接触或分离时产生的震动,导致短时间内多次导通和断开,从而可能被系统误认为是多次按键操作。

为了解决这个问题,可以采用硬件和软件两种去抖动方案:

  1. 硬件去抖动:

    使用RC积分电路:通过电阻和电容的组合,将开关的抖动信号平滑为一个较为稳定的电平输出。具体来说,开关闭合时,电容开始充电,电压逐渐上升。由于充电需要一定的时间常数,短时间的抖动不会使电压达到触发电平,从而抑制抖动。
  2. 软件去抖动:

    采用计时法:在检测到按键状态变化时,启动一个定时器(例如20ms)。在定时器到期后重新检测按键状态。如果此时按键状态与定时器启动时一致,则确认按键状态变化有效。这种方法通过软件的方式来过滤掉短时间内的状态变化,从而避免抖动造成的误触发。

5.3.2 串口通信模块子程序设计

串行接口是一种能够将CPU发送的并行数据字符转换为串行数据流,并将接收到的串行数据流转换回并行数据字符供给CPU的电路装置。通常,我们把这种实现双向转换功能的电路称为串行接口电路。

在本设计中,串口通信被广泛用于多个设备之间的交互,包括蓝牙模块与51单片机的通信、51单片机与STM32单片机的通信,以及STM32单片机与MP3-FLASH-16P模块的通信。这些设备间的通信都是通过串行接口进行的。

针对蓝牙模块与51单片机的通信,51单片机与STM32单片机的通信,可以认为传输的信息是相同的。因此,这里采用统一的通信协议进行设计。通信链路中的控制命令主要涉及曲目选择、音量调节、播放和停止功能。待选择的曲目一共有6首,通过分析可知,整个系统仅需传递9种不同的信息即可涵盖所有命令。经过设计优化后,发现仅需4位数据就可以实现一套设备的所有控制命令,而一个字节(8位数据)便能完成两套设备的完整控制。命令帧格式如表5-1所示。

 可以看到,低位的 4 个字节和高位的 4 个字节分别控制两套系统,BIT3 和 BIT7 用来控制播放(1)/停止(0),BIT0-BIT2 和 BIT4-BIT6 分别前播放的音乐和音量的增大减小,其中 0~5(000~101)用于选择第 1—6 首音乐, 6(110)表示音量增大,7(111)表示音量减小。然后将 BIT7~BIT0 组合成串口 通讯控制命令,其控制命令用十六进制表示。控制命令第一位代表 A 分区工作 状态,第二位代表 B 分区工作状态。串口通讯控制命令详情如表 4-2

本设计中 STM32 单片机即通过串行接口和 MP3-FLASH-16P 模块进行通信, 该 MP3 模块的功能非常强大,具有丰富的指令,可以完成所有的常规播放功能, 比如指定文件夹内的文件播放、音量的调节和开始播放、停止播放等功能,都通 过串口通讯来进行控制。串口通讯的参数为:9600 波特率,1 位起始位,1 位停 止位和 8 位数据位[10]。 STM32 单片机与 MP3 模块进行串口通讯的流程如上图 4-4 所示

5.3.3 蓝牙串口助手配置过程

可以看出本设计是通过使用HC-02蓝牙模块,实现手机对控制端的控制,并进一步对两个发射端分别进行操作。为了便于操作,手机端可以使用蓝牙串口助手来接收和发送蓝牙命令。

在设计中,HC-02模块采用串口透明传输方式,这样可以简化通讯过程,并且减少开发复杂性。汇承公司还提供了一个配套的软件——蓝牙串口助手,这使得用户很方便地进行蓝牙设备的管理和指令发送。

启动过程是这样的:当控制端上电后,HC-02模块上的指示灯会闪烁,这表明模块已经正常启动和工作。随后,用户可以打开蓝牙串口助手软件,经过短暂的等待后,会在可连接的设备列表中看到“HC-02”的图标。这说明手机已经检测到HC-02模块,可以进行连接。

图 5-5 HC 蓝牙助手搜索 

之后点击“HC-02”这一图标,根据提示输入密码,这里使用默认密码“1234” 即可,然后进入蓝牙串口助手与模块进行通信的界面,如下图 5-6 所示。

图 5-6 蓝牙助手与模块的通信界面

在很多系统中,使用16进制是为了更清晰和简洁地表示数值,特别是在涉及到硬件或低级通信协议的场合。

图 5-7 设置为 HEX发送

六、系统装置测试:

6.1 实物介绍

你提到的这个系统利用光调制技术实现了音乐的无线传输和播放,并通过主控端与发射端的通信来实现对接收端的控制。接下来,我将为你解释你最后一段问题中的关键点——为什么在蓝牙连接成功后,人们可以随意控制不同分区的音乐播放

这主要是因为该系统的主控端使用了 51 单片机,而发射端则使用了 STM32 核心控制器。两者之间通过串口通信实现了联动操作。主控端通过发送配置好的指令来与发射端的 STM32 控制器进行通讯,这些指令可以是控制音乐播放、暂停、切换音乐、音量调节等操作。发射端接收到这些命令后,STM32 控制器会进一步控制音乐信号的传输和发射。

具体来说:

  1. 主控端(51 单片机)发送命令:当用户在蓝牙设备上进行操作时,例如播放、暂停音乐或切换音轨,主控端的51单片机会根据用户输入生成相应的指令。

  2. 发射端(STM32 控制器)接收并执行命令:这些命令通过串口通讯发送到发射端的STM32控制器,STM32根据指令控制音频信号的传输,进而控制接收端的音乐播放。

  3. 接收端播放音乐:接收端可以分别安装在不同房间,利用光敏三极管接收光调制信号,解调出音频信号,最终通过扬声器播放音乐。

这种系统架构的优势在于,蓝牙连接后,用户可以在任意分区控制不同房间(分区1和分区2)的音乐播放,因为命令是通过串口控制发射端的,而发射端可以分别控制不同区域的音乐播放,实现了分区播放的灵活性和独立性。

图 6-1 系统装置 

实物图如图 6-2,下面将基于上文所述开始进行实物测试。以下测试环境在 光照、温度、介质一定下进行。

图 6-2 系统装置实物图 

6.2 对其中一套发射端和接收端进行装置测试 

发射端与接收端放置如上图 5-2 所示。首先将两个 9V1A 的电源适配器插到 发射板和接收板的电源孔内,给两个板子都通电。发现发射端的 LED 点亮, STM32 单片机、TDA2030A 功放模块正常工作,此时就可以点击发射端左上角 的 9 个按键。当第 1~6 个按键按下时,能听到六首不同的音乐。当第 7~8 个按 键按下时,可以对音量进行加减操作。当第 9 个按键按下时,音乐终止播放。

在测试的过程中,发现本系统装置音质与音量会随着,发射端与接收端距离变化。

6.2.1 发射端与接收端距离 3cm

当发射端与接收端距离为 3cm 时。此时发射端与接收端距离过近,原因是 距离太近,光照太强,会导致光敏三极管处于饱和状态,不能处于很好的导通状 态,也就不能很好地通信。播放的声音杂音明显,音质较差,音量巨大且部分信 号失真。如图 6-3 所示:

图 6-3 发射端与接收端距 

6.2.2 发射端与接收端距离 35cm

通过不断调整发射端和接收端之间的距离,最后发现当两者距离为 35 cm 时, 光照适中,光敏三极管处于放大状态,播放音频基本没有杂音,音质达到最佳状 态,音量适中且信号基本没有失真。当光源被遮挡住,音乐停止播放,说明该系 统的光源遭到影响时,信号无法正常传输。如图 6-4 所示 

图 6-4 发射端与接收端距离 

6.2.3 发射端与接收端距离 150cm

图 6-5 发射端与接收端距离 

增大发射端与接收端之间的距离,音质无明显变化,但音量会随着距离增大 而减小,当距离超过 50cm 以外,音乐声会逐渐消失,直到 150cm,光敏三极管 会进入截止状态。音乐会完全消失。如图 6-5 所示 

6.3 对整体系统装置进行测试

6.3.1 将电源接口接上电源,给硬件上电

首先,将两个9V1A电源适配器分别插入两个发射板或两个接收板的电源接口,给两个板子供电。可以观察到,两个发射端的LED灯亮起,同时,两套发射接收系统中的STM32单片机、MP3模块、TDA2030A功放模块以及主控端的蓝牙模块的指示灯也亮起。此时,各个模块均处于待机状态,准备接收指令以开始工作。工作状态如图6-6所示。

图 6-6 所有模块正常工作

6.3.2 按键发送不同指令时系统装置的工作状态 

首先,将两个额定为9V1A的电源适配器分别插入两个发射端的电源接口,为两个电路板供电。此时,发射端的LED灯会亮起,表明STM32单片机和TDA2030A功放模块工作正常。在这种情况下,可以随意按下两个分区的九个按键。当按下第1到第6个按键时,会播放六首不同的音乐。按下第7和第8个按键时,可以进行音量的加减调整。按下第9个按键时,音乐会停止播放。两个分区可以独立操作,也可以同时实现上述功能。其工作状态如图6-7所示。

图 6-7 按下按键后 A 区(左)、B 区(右)工作状态 

6.3.3 蓝牙发送不同指令时系统装置的工作状态 

在配置HEX发送时,按照表4-3的格式发送80(十六进制)指令,即一个字节的八位二进制数据为1000 0000。这将使得A区(左边的接发设备)播放第一首音乐,而B区(右边的设备)则不播放音乐。如果输入指令为90、A0、B0、C0或D0,则A区将分别播放第二、第三、第四、第五和第六首音乐,同时B区依然不播放音乐。在系统装置测试中,这些指令能够实现相应的功能。工作状态如图5-8所示。

图 6-8 输入 80 指令后系统装置工作状态

根据表 5-3 的指示,当发送十六进制指令 08 时,系统将以一个字节(八比特位)的格式发送数据,该数据为 0000 1000。此时,A 区(左边的设备)将不会播放音乐,而 B 区(右边的设备)将播放第一首音乐。如果输入指令为 09、0A、0B、0C 或 0D,则 A 区依旧不播放音乐,而 B 区将分别播放第二、第三、第四、第五或第六首音乐。在系统装置的测试中,这些指令均能实现相应的功能。工作状态如图 6-9 所示。 

图 6-9 输入 08 后指令后系统装置工作状 

根据表 5-3 的指令设置,将HEX发送设置为88,即发送一字节的八位二进制数据:1000 1000。这样,A区(左侧设备)和B区(右侧设备)将同时播放第一首音乐。如果输入指令为99、AA、BB、CC、DD,那么A区和B区将同时播放第二、第三、第四、第五和第六首音乐。在系统装置测试中,这些指令都能够实现相应的功能。工作状态可以参考图6-10。

图 6-10 输入 88 指令后系统工作状态 

根据你的描述,HEX 指令 "EE" 应该被发送,这个指令的二进制形式是 11101110。根据你的说明,A 区和 B 区的设备将同时增加音量。

在测试系统装置时,该指令应该能够实现相应的功能。由于我无法访问或查看图 6-11,所以无法看到具体的工作状态或具体细节。如果有其他问题或需要进一步的解释,请告诉我,我会尽力帮助。

图6-11 输入 EE 指令后系统装置工作状态

配置好 HEX 发送功能,按照表 5-3 发送 FF 的十六进制指令,即一个字节的八个比特位为 1111 1111。在 A 区(左侧接发设备)和 B 区(右侧接发设备)中,同时降低音量。在系统设备测试中,以上指令能够成功实现预期功能。工作状态如图 6-12 所示。

图 6-12 输入 FF 指令后系统装置工作状态

要发送HEX指令,根据表5-3,发送一个字节(八个比特)的十六进制指令“00”,即二进制形式为“0000 0000”。在执行这个指令后,A区(左侧设备)和B区(右侧设备)会同时停止播放。在系统的测试过程中,这个指令能够实现预期的功能。工作状态可以参考图6-13。

图 6-13 输 00 指令后系统装置

 设置HEX发送功能时,根据表5-3,我们可以发送0E的十六进制指令,这一字节的数据为0000 1110。此时,A区(左侧的接收设备)将暂停播放,而B区(右侧的接收设备)将增加音量。如果输入指令0F,A区将暂停播放,B区则会降低音量。若输入指令E0,A区将增加音量,而B区将暂停播放。输入指令F0时,A区会降低音量,B区同样会暂停播放。在设备系统测试中,这些指令均能成功实现预定的功能。有关工作状态的详细信息,请参见图6-14。

 图6-14 输入 0E 指令后系统装置工作状态

设置好 HEX 发送后,按照表 5-3 发送 8E 十六进制指令,其二进制表示为 1001 1110。此时,A 区(左侧的接收设备)将开始播放第一首音乐,而 B 区(右侧的接收设备)则会增加音量。如果输入指令为 9E、AE、BE、CE 或 DE,A 区会依次播放第二、第三、第四、第五和第六首音乐,同时 B 区也会继续增加音量。在系统装置测试中,上述指令均能成功实现其对应的功能。工作状态如图 6-15 所示。 

图 5-15 输入 8E 指令后系统装置工作状态 

在音频系统测试中,通过增加A区的音量来检查系统的各项功能是否正常。在这一过程中,选择了五、六首音乐进行播放。测试的工作状态可以参考图6-16所示。

 图 6-16 输入 E8 指令后系统装置工作状态

七、结果分析 

通过对发射端和接收端进行装置测试,在光照、温度及介质恒定的条件下,通过调整两者之间的距离,得到了以下结果分析:

当距离过近时,出现信号失真和音质下降的现象。经过分析,这是由于发射结电压过大,导致光敏三极管进入饱和状态,集电极电流不再与基极电流按比例增加。相反,当距离过远时,信号逐渐减弱,音乐逐渐消失。这是因为发射结电压过小,基极电流减少,直到发射结电压低于PN结的导通电压,基极电流变为零,光敏三极管处于截止状态,导致音乐完全消失。

经过进一步测试发现,当接收端与发射端相距35厘米时,音频播放几乎没有杂音,音质最佳且音量适中,信号失真极小。分析表明,此时光敏三极管所受光照适中,发射结电压适宜,使其处于最佳放大状态,符合光敏三极管的工作特性曲线。同时,系统达到了预期的30~40厘米的传输距离目标。当光源被遮挡时,音乐停止播放,说明系统对光源的依赖较强,一旦光源受影响,信号传输就会中断,体现了该装置较好的安全性和保密性。

对于整体系统装置的进一步测试中,手机能够正常连接蓝牙,通过蓝牙模块发送指令控制系统工作。蓝牙模块与51单片机、51单片机与STM32单片机之间的通信也正常。测试了表4-1中所有的串口通信控制命令,每一条控制命令都能实现对应的功能,诸如曲目选择、音量调节、播放和停止等功能均得到了验证。说明该系统的软件设计、硬件设计以及光调制功能设计均无问题。此外,分区播放音乐的功能也顺利实现。

详细资源:毕设&课设&项目&竞赛-基于STM32+RTOS音频光通信设计与实现(完整工程资料源码).zip资源-CSDN文库

资料文件夹:

源码文件夹:

 

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

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

相关文章

【docker】docker network 网络

docker network 网络 Docker 为什么需要网络管理docker 网络架构简介CNMLibnetwork驱动 常见网络类型docker 网络管理命令 Docker 为什么需要网络管理 容器的网络默认与宿主机及其他容器都是相互隔离, 但同时我们也要考虑下面的一些问题, 比如 多个容器…

Excel图表生成:自动化创建与修改Excel图表的技术指南

目录 引言 Excel图表基础 图表的作用与类型 Excel图表制作的基本步骤 自动化创建Excel图表 使用VBA宏自动化创建图表 自动化创建柱状图 自动化创建折线图 使用Python和第三方库自动化创建图表 安装必要的库 编写Python代码 修改Excel图表 修改图表类型和样式 自动…

网络学习-eNSP配置NAT

NAT实现内网和外网互通 #给路由器接口设置IP地址模拟实验环境 <Huawei>system-view Enter system view, return user view with CtrlZ. [Huawei]undo info-center enable Info: Information center is disabled. [Huawei]interface gigabitethernet 0/0/0 [Huawei-Gigabi…

什么是COB超微小间距会议一体机?LED智能会议一体机重塑会议体验

在当今这个快节奏、高效率的时代&#xff0c;会议作为企业日常运营中不可或缺的一环&#xff0c;其效率与体验直接影响着企业的决策速度与团队协作能力。随着科技的飞速发展&#xff0c;传统的会议设备已难以满足现代会议室的多元化需求&#xff0c;LED智能会议一体机&#xff…

0基础跟德姆(dom)一起学AI Python进阶09-算法和数据结构

* 数据结构介绍 * 列表 * 链表 * 算法介绍 * 排序相关(冒泡, 插入, 选择, 快速排序) --- 1.数据结构和算法简介 * 程序 大白话翻译, **程序 数据结构 算法** * 数据结构 指的是 **存储, 组织数据的方式.** * 算法 指的是 **为了解决实际业务问题而思考 思路和方法…

气膜体育馆投资分析:未来体育设施的新方向—轻空间

随着全民健身运动的普及和城市化进程的加快&#xff0c;现代体育馆的需求日益增长。相较于传统建筑&#xff0c;气膜体育馆凭借其高性价比、快速搭建和灵活性&#xff0c;逐渐成为投资者关注的新型体育设施。从投资角度来看&#xff0c;气膜体育馆具备许多优势&#xff0c;能够…

IDEA怎么让控制台自动换行

IDEA怎么让控制台自动换行 操作流程 菜单>File>Settings>Editor>General>Console>勾选Use soft wraps in console 换行效果

广州市小学信息技术教案和课件

第一册 第二册 第三册 需要的加v

突发!如何应对微信小程序与公众号下发统一消息接口调整

前言 国庆节前夕&#xff0c;被同事突然Q到&#xff1a;“咱们的公众号消息推送是不是要修改&#xff1f;”&#xff0c;我一脸茫然&#xff0c;紧接着&#xff0c;他直接甩给我一个链接&#xff0c;我打开后一看。一瞬间愉快的心情完全被打乱了。 但是&#xff0c;不要慌&…

基于开源WQ装备数据的知识图谱全流程构建

随着大数据和人工智能技术的快速发展&#xff0c;构建领域特定的知识图谱已成为信息管理和决策支持的重要手段。武器装备知识图谱不仅能够对复杂的武器系统进行结构化展示&#xff0c;还可以通过关系推理揭示武器与装备之间的潜在联系。 1、技术路线 本文将详细介绍如何基于开…

Leetcode第414周赛第二题:3281. 范围内整数的最大得分

一&#xff1a;题目&#xff1a; 给你一个整数数组 start 和一个整数 d&#xff0c;代表 n 个区间 [start[i], start[i] d]。 你需要选择 n 个整数&#xff0c;其中第 i 个整数必须属于第 i 个区间。所选整数的 得分 定义为所选整数两两之间的 最小 绝对差。 返回所选整数的…

程序员如何写笔记?

word。没错&#xff0c;我也看了网上一大堆软件&#xff0c;还有git管理等等。个人认为如果笔记只是记录个人的经验积累&#xff0c;一个word就够了&#xff0c;那些notepad&#xff0c;laTex个人觉得不够简练。word。 1.word可以插入任何文件附件(目前最大的word 200MB也没出现…

Python编码系列—Python社区驱动开发:共创开源生态的力量

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

Furion友好异常

一、一图流 二、注册友好异常服务 在 Furion 框架中&#xff0c;友好异常处理服务可以通过 AddFriendlyException 方法进行注册&#xff0c;这个方法通常在项目的启动配置中使用。 using Microsoft.Extensions.DependencyInjection;namespace YourNamespace {[AppStartup(800…

组件拆分综合案例——商城首页

欢迎大家订阅【Vue2Vue3】入门到实践 专栏&#xff0c;开启你的 Vue 学习之旅&#xff01; 文章目录 一、传统的 HTML/CSS 写法二、现代的 Vue.js 组件化写法 本篇文章参考黑马程序员 【组件拆分综合案例——商城首页】 一、传统的 HTML/CSS 写法 App.vue文件&#xff1a; &l…

JAVA便捷同城圈信息汇聚服务预约任务发布平台系统小程序源码

​便捷同城圈&#xff0c;信息汇聚服务预约任务发布平台系统 &#x1f3d9;️ 开篇&#xff1a;同城生活&#xff0c;一键触达 在这个快节奏的城市里&#xff0c;你是否经常为找不到靠谱的服务、错过重要的信息或是有小任务却找不到人帮忙而烦恼&#xff1f;别担心&#xff0c…

运维学习————GitLab的搭建和使用

目录 一、简介 1、概念 2、 好处 3、优势 二、 下载安装 1、准备工作 2、下载安装 3、配置 三、常用命令 四、GitLab的使用 1、解决无法访问 ​编辑2、修改密码 3、gitlab结合linux的使用 1、使用用户名和密码操作 2、使用ssh秘钥操作 4、卸载GitLab 5、gi…

铁路故障多分类数据集(猫脸码客 第183期 )

铁路故障多分类数据集研究&#xff1a;基于深度学习的ECARRNet模型 引言 东南亚地区以其庞大的铁路网络著称&#xff0c;这一基础设施在促进区域经济发展、加强国家间联系方面发挥着不可替代的作用。然而&#xff0c;随着铁路网络的不断扩展和运营时间的增长&#xff0c;铁路…

OpenCV结构分析与形状描述符(11)椭圆拟合函数fitEllipse()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆&#xff0c;该椭圆在最小二乘意义上最好地拟合一组2D点。它返回一个内切椭圆的旋转矩形。使…

oracle TAB$ 基表删除恢复

接客户服务请求&#xff0c;说是一个好久没有用的数据库在打开时遭遇ora-00600 16703 1403 28 错误&#xff0c;这中一看就是oracle 的tab$基表被删除了。 客户环境 aix 平台&#xff1b; 索性数据库比较小只有10G左右&#xff0c;懒得的弄bbed&#xff0c;直接oracle dul 抽…