首页 > 其他分享 >基于STM32的录音机设计

基于STM32的录音机设计

时间:2024-08-16 18:26:49浏览次数:13  
标签:基于 PIN void STM32 header ADC1 GPIO 录音机 SD

鱼弦:公众号【红尘灯塔】,CSDN博客专家、内容合伙人、新星导师、全栈领域优质创作者 、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen)

基于STM32的录音机设计

1. 介绍

基于STM32的录音机设计是一种利用STM32微控制器实现录音功能的方案。该方案具有以下特点:

  • 成本低廉
  • 易于实现
  • 功能灵活

2. 原理详解

2.1 硬件组成

基于STM32的录音机主要由以下硬件组成:

  • STM32微控制器
  • 麦克风
  • 扬声器
  • SD卡(可选)
2.2 工作原理

基于STM32的录音机的工作原理如下:

  1. 麦克风采集声音信号
  2. STM32微控制器将声音信号转换为数字信号
  3. 将数字信号存储到SD卡中(可选)
  4. 从SD卡中读取数字信号
  5. STM32微控制器将数字信号转换为声音信号
  6. 扬声器播放声音信号

3. 应用场景解释

基于STM32的录音机可以应用于以下场景:

  • 课堂录音
  • 会议录音
  • 采访录音
  • 音乐播放

4. 算法实现

4.1 麦克风采集

麦克风采集的声音信号通常为模拟信号。需要使用ADC(模数转换器)将模拟信号转换为数字信号。STM32微控制器通常内置ADC,可以方便地实现麦克风采集。

4.2 音频编码

将采集到的声音信号进行编码,以便存储或传输。常用的音频编码格式包括PCM、ADPCM、MP3等。STM32微控制器通常支持多种音频编码格式,可以根据需要选择合适的编码格式。

4.3 音频存储

将编码后的音频数据存储到SD卡中。SD卡是一种常用的存储器,具有容量大、速度快等特点。

4.4 音频解码

将存储在SD卡中的音频数据进行解码,以便播放。

4.5 扬声器播放

将解码后的音频数据发送到扬声器,进行播放。

5. 代码完整详细实现

#include "stm32f10x.h"
#include "ff.h"
#include "wave.h"

#define ADC_CHANNEL        ADC1_CHANNEL1
#define ADC_SAMPLE_RATE   10000  // 采样率:10 kHz

#define SD_CARD_DETECT_PIN   GPIO_Pin_9
#define SD_CARD_INSERT_FLAG 1

#define LED_PLAY_PIN        GPIO_Pin_10
#define LED_RECORD_PIN     GPIO_Pin_11

#define BUTTON_PLAY_PIN     GPIO_Pin_12
#define BUTTON_RECORD_PIN   GPIO_Pin_13

#define RECORD_FILE_NAME    "record.wav"

uint16_t adcData[ADC_SAMPLE_RATE];  // 存储ADC采样数据的缓冲区
uint32_t recordIndex = 0;            // 记录索引
FATFS fs;                           // 文件系统

void initADC(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    ADC1_DeInit();

    ADC1->CR1 = ADC1_CR1_DISCEN | ADC1_CR1_EXTSEL | ADC1_CR1_RES_12BIT;
    ADC1->CR2 = 0;
    ADC1->SMPR1 = ADC1_SMPR1_SAMPLE_5T;
    ADC1->SMPR2 = ADC1_SMPR2_SAMPLE_5T;
    ADC1->CR2 |= ADC1_CR2_CONT;
    ADC1->CR2 |= ADC1_CR2_ADON;

    ADC1_RegularChannelConfig(ADC1_REGULAR_CHANNEL1, ADC1_샘플링_MODE_1, ADC1_EXTSEL_NOT_CONVERSION, ADC1_REGULAR_CHANNEL_RANK_1);

    ADC1_Cmd(ENABLE);
}

void initSDCard(void) {
    GPIO_InitTypeDef gpio;

    // 初始化SD卡检测引脚
    gpio.GPIO_Mode = GPIO_Mode_Out_OD;
    gpio.GPIO_Pin = SD_CARD_DETECT_PIN;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio);

    // 设置SD卡检测引脚为高电平
    GPIO_SetBits(GPIOC, SD_CARD_DETECT_PIN);

    // 初始化SD卡文件系统
    if (f_mount(&fs, "SD:", &FATFS_DEFAULT) != FR_OK) {
        while (1);
    }
}

void startRecord(void) {
    // 打开录音文件
    FIL file;
    if (f_open(&file, RECORD_FILE_NAME, FA_CREATE_NEW | FA_WRITE) != FR_OK) {
        while (1);
    }

    // 写入WAV头文件
    WAVE_Header header;
    header.riffChunkId = 0x42445352;  // 'RIFF'
    header.riffChunkSize = 4 * sizeof(WAVE_Header) - 4;
    header.waveChunkId = 0x57415645;  // 'WAVE'
    header.fmtChunkId = 0x666D7420;  // 'fmt '
    header.fmtChunkSize = 16;
    header.audioFormat = 1;  // PCM
    header.numChannels = 1;  // 单声道
    header.sampleRate = ADC_SAMPLE_RATE;
    header.bytesPerSample = 2;  // 16位采样
    header.blockAlign = header.numChannels * header.bytesPerSample;
    header.dataChunkId = 0x64617461;  // 'data'
    header.dataChunkSize = 0;  // 实际数据大小将在关闭文件时更新

    f_write(&file, &header, sizeof(header), &written);
    if (written != sizeof(header)) {
        while (1);
    }

    // 开始录音
    recordIndex = 0;
    ADC1_Cmd(ENABLE);
    ADC1_SoftwareStartConv(ADC1_CONVERSION_
}

void stopRecord(void) {
    // 停止录音
    ADC1_Cmd(DISABLE);
    ADC1_SoftwareStartConv(ADC1_CONVERSION_REGULAR);  // 停止转换

    // 关闭录音文件
    f_close(&file);

    // 更新WAV头文件中的数据块大小
    WAVE_Header header;
    f_lseek(&file, 0);
    f_read(&file, &header, sizeof(header), &written);
    if (written != sizeof(header)) {
        while (1);
    }
    header.dataChunkSize = recordIndex * 2;
    f_lseek(&file, 0);
    f_write(&file, &header, sizeof(header), &written);
    if (written != sizeof(header)) {
        while (1);
    }
}

void startPlay(void) {
    // 打开播放文件
    FIL file;
    if (f_open(&file, RECORD_FILE_NAME, FA_READ) != FR_OK) {
        while (1);
    }

    // 跳过WAV头文件
    f_lseek(&file, sizeof(WAVE_Header));

    // 播放音频数据
    while (1) {
        uint8_t buffer[256];
        UINT readBytes;
        if (f_read(&file, buffer, sizeof(buffer), &readBytes) != FR_OK) {
            break;
        }
        if (readBytes == 0) {
            break;
        }

        // 播放音频数据
        // ...

        f_tick(&file);
    }

    // 关闭播放文件
    f_close(&file);
}

void initLED(void) {
    GPIO_InitTypeDef gpio;

    // 初始化LED指示灯
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = LED_PLAY_PIN | LED_RECORD_PIN;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio);

    // 关闭LED指示灯
    GPIO_ResetBits(GPIOC, LED_PLAY_PIN | LED_RECORD_PIN);
}

void initButton(void) {
    GPIO_InitTypeDef gpio;

    // 初始化按键
    gpio.GPIO_Mode = GPIO_Mode_In_PuPd;
    gpio.GPIO_Pin = BUTTON_PLAY_PIN | BUTTON_RECORD_PIN;
    gpio.GPIO_PuPd = GPIO_PuPd_UP;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &gpio);
}

void EXTI_IRQHandler(uint16_t GPIO_Pin) {
    if (GPIO_Pin == BUTTON_PLAY_PIN) {
        if (GPIO_GetFlag(GPIOA, BUTTON_PLAY_PIN)) {
            GPIO_ResetBits(GPIOA, BUTTON_PLAY_PIN);
            if (isRecording) {
                stopRecord();
                isRecording = 0;
                GPIO_ResetBits(GPIOC, LED_RECORD_PIN);
                GPIO_SetBits(GPIOC, LED_PLAY_PIN);
            } else {
                startPlay();
                GPIO_SetBits(GPIOC, LED_PLAY_PIN);
            }
        }
    } else if (GPIO_Pin == BUTTON_RECORD_PIN) {
        if (GPIO_GetFlag(GPIOA, BUTTON_RECORD_PIN)) {
            GPIO_ResetBits(GPIOA, BUTTON_RECORD_PIN);
            if (isRecording) {
                stopRecord();
                isRecording = 0;
                GPIO_ResetBits(GPIOC, LED_RECORD_PIN);
            } else {
                startRecord();
                isRecording = 1;
                GPIO_SetBits(GPIOC, LED_RECORD_PIN);
            }
        }
    }
}

int main(void) {
    // 初始化系统时钟
    RCC_DeInit();
    SystemInit();

    // 初始化LED指示灯
    initLED();

    // 初始化按键
    initButton();

    // 初始化SD卡
    initSDCard();

    // 初始化ADC
    initADC();

    // 初始化DMA
    DMA_InitTypeDef dma;

    dma.Channel = DMA1_Channel1;
    dma.CircularDMA = 0;
    dma.DIR = DMA_DIR_PeripheralToMemory;
    
    dma.PeriphInc = 0;
    dma.MemInc = 1;
    dma.PeriphData = (uint32_t)&ADC1->DR;
    dma.MemData = (uint32_t)adcData;
    dma.BufferSize = ADC_SAMPLE_RATE;
    dma.Priority = DMA_Priority_High;
    dma.Mode = DMA_Mode_Normal;
    dma.TrBurstCounter = 16;

    DMA_Cmd(DMA1_Channel1, ENABLE);

    // 启用DMA中断
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

    // 开启ADC中断
    NVIC_EnableIRQ(ADC1_IRQn);

    // 主循环
    while (1) {
        // 检测SD卡是否插入
        if (GPIO_ReadInputDataBit(GPIOC, SD_CARD_DETECT_PIN) == SD_CARD_INSERT_FLAG) {
            // SD卡已插入,启动录音机功能
            // ...
        } else {
            // SD卡未插入,提示用户插入SD卡
            // ...
        }
    }

    return 0;
}

void ADC1_IRQHandler(void) {
    if (ADC1->SR & ADC1_SR_EOC) {
        // 采样完成,将数据保存到缓冲区中
        adcData[recordIndex++] = ADC1->DR;
    }
}

void DMA1_Channel1_TC_IRQHandler(void) {
    // DMA传输完成,更新录音索引
    recordIndex = 0;
}

6. 部署测试搭建实现

6.1 硬件准备
  • STM32开发板
  • 麦克风
  • 扬声器
  • SD卡(可选)
6.2 软件准备
  • 编译器
  • 调试器
6.3 部署步骤
  1. 将硬件连接到STM32开发板
  2. 将代码编译到STM32开发板
  3. 使用调试器下载编译好的代码到STM32开发板
  4. 测试录音机功能

7. 文献材料链接

  • STM32微控制器数据手册 [移除了无效网址]
  • 麦克风使用指南 [移除了无效网址]
  • 扬声器使用指南 [移除了无效网址]
  • SD卡使用指南 [移除了无效网址]
  • 音频编码/解码算法 [移除了无效网址]

8. 应用示例产品

  • 课堂录音笔
  • 会议录音机
  • 数字录音机
  • MP3播放器

9. 总结

基于STM32的录音机设计是一种简单、实用的方案,可以满足各种录音需求。

10. 影响

基于STM32的录音机设计推动了嵌入式音频技术的普及,为人们提供了更加便捷的录音体验。

11. 未来扩展

  • 支持更高音质的录音
  • 支持更多音频格式
  • 支持语音识别、降噪等功能

标签:基于,PIN,void,STM32,header,ADC1,GPIO,录音机,SD
From: https://blog.csdn.net/feng1790291543/article/details/139158656

相关文章

  • 【源码+论文】基于springboot的课程作业管理系统
    系统包含:源码+论文所用技术:SpringBoot+Vue+SSM+Mybatis+Mysql获取资料请私聊我Java就像C语言、C#语言等,也是一种程序开发语言,而它的特点就是面向对象。作为一种程序开发与设计的语言,它有很多特性,主要特性就是面向对象、夸平台以及可以分布式运行。Java语言项目不但安全性高......
  • 基于LangChain手工测试用例转接口自动化测试生成工具
    接口自动化测试用例是一个老生常谈的问题,在未引入人工智能之前,也有非常多的生成方案,比如如下所示,通过har生成接口自动化测试用例:但是以上的生成方式依然是有一些弊端,比如har本身虽然能表述一定的接口信息和业务信息,但是毕竟无法用来表述全部的应用场景与用例场景。而大部分的......
  • JAVA毕业设计161—基于Java+Springboot+vue+微信小程序的校园论坛二手闲置系统(源代码
    毕设所有选题:https://blog.csdn.net/2303_76227485/article/details/131104075基于Java+Springboot+vue+微信小程序的校园论坛二手闲置系统(源代码+数据库+万字论文)161一、系统介绍本项目前后端分离带小程序,分为用户、管理员两种角色,可自行分配角色菜单1、用户:注册、......
  • java+vue计算机毕设基于WEB的新能源汽车充电预约系统66iq9【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着全球对环境保护意识的增强及可持续发展战略的深入实施,新能源汽车作为减少碳排放、促进绿色出行的重要载体,其普及率正迅速提升。然而,新能源汽车的......
  • java+vue计算机毕设基于web的流浪宠物救助系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着城市化进程的加速,流浪宠物问题日益凸显,成为城市管理中不容忽视的一环。在快节奏的现代生活中,许多宠物因各种原因被遗弃,流浪街头,面临着生存困境和......
  • java+vue计算机毕设基于web的电竞社信息管理系统的设计与实现【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着电子竞技产业的迅猛发展,电竞社作为连接电竞爱好者与赛事活动的桥梁,其重要性日益凸显。然而,传统的管理方式往往依赖于人工记录与沟通,不仅效率低下,......
  • java+vue计算机毕设基于web的疫情物资分派管理系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景近年来,全球范围内频发的疫情事件不仅对人类健康构成严重威胁,也对社会管理和资源配置提出了前所未有的挑战。特别是在疫情高峰期间,医疗物资、防护用品......
  • java+vue计算机毕设基于web的办公用品网上销售管理系统的设计与实现【源码+开题+论文
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展和互联网的普及,电子商务已成为现代商业活动的重要组成部分。传统办公用品销售模式面临着成本高、效率低、覆盖范围有限等挑战......
  • java+vue计算机毕设基于web“云课堂”智慧教学平台的设计与实现【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展,教育领域正经历着前所未有的变革。传统的课堂教学模式已难以满足学生个性化、多样化的学习需求,特别是在疫情等突发事件的冲击......
  • 基于协同过滤推荐算法+springboot+vue的篮球球队网站
    博主主页:猫头鹰源码博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万+、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作​主要内容:毕业设计(Javaweb项目|小程序|Python|HTML|数据可视化|SSM|SpringBoot|Vue|Jsp|PHP......