基于STM32的录音与播音
基于STM32的录音与播音
设计方案
本设计通过STM32的内置ADC加一个麦克风和放大电路(可以在网上买模块)实现音频的采集,然后存放在SD卡中(这里可以参考我之前的博客FATFS文件系统),然后再读取SD卡里存放的数据通过单片机的内置DAC接一个放大电路和一个喇叭(可以在网上买模块)进行音频播放(参考我前面的博客)。再进行ADC采集和DAC输出时是借助定时器中断,设置定时器的频率就可以设置ADC和DAC的频率了。
通过STM32CUBEMX配置工程
1、配置SDIO和FATFS
参考我前面的博客。https://blog.csdn.net/qq_53000374/article/details/126390968
2、配置DAC和定时器
参考我前面的博客。https://blog.csdn.net/qq_53000374/article/details/126272701
3、配置ADC和定时器
这里只需要配置ADC,定时器和DAC共用一个。
其他的不用管。
添加代码
//main前面
#include "string.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
#define KEY0 HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)
#define KEY1 HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)#define val_count_max 5000 //最大采样个数
uint16_t val_count=0; //实际采样个数uint16_t val_data[val_count_max]={0};
uint16_t val_data_buff[val_count_max]={0};#define DAC_Count 4096
uint8_t DAC_Data[5000]={0};uint16_t DAC_F=2;
volatile uint16_t Time_Count=0;char* ch_tab="12345";char read_buff[100]={0};FATFS fs; //文件系统工作区指针
FIL fp; //文件对象结构指针
FRESULT f_res; //函数放回结果
uint16_t fnum; /* 文件成功读写数量 */uint16_t fp_num; //文件大小
UINT fp_rw_num; //实际文件读写字节数uint8_t fp_wriet_flag=0; //SD卡写标志
uint8_t sound_record_flag=0; //录音标志uint8_t fp_read_flag=0; //SD卡读标志
uint8_t play_record_flag=0; //播音标志uint8_t key0_down_flag=0; //按键按下一次标志,
uint8_t key1_down_flag=0; //按键按下一次标志,uint16_t fp_memory=0;char Read_Data[DAC_Count]={0};
/* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */void KEY_Scanf()
{if(KEY0 == 0) //录音开始和停止{HAL_Delay(10);if(KEY0==0){while(!KEY0);key0_down_flag=0;sound_record_flag = !sound_record_flag; //0:停止 1:开始}}if(KEY1 == 0){HAL_Delay(10);if(KEY1==0){while(!KEY1);key1_down_flag=0;play_record_flag = !play_record_flag;}}
}void KEY_Handle(void)
{if((sound_record_flag != 0) && (key0_down_flag==0)) //开始录音{key0_down_flag=1;val_count=0;HAL_TIM_Base_Start_IT(&htim3);HAL_ADC_Start(&hadc1);}else if((sound_record_flag == 0) && (key0_down_flag==0)) //停止录音{key0_down_flag=1;HAL_TIM_Base_Stop_IT(&htim3);HAL_ADC_Stop(&hadc1);f_close(&fp);f_mount(NULL,"0:",1); //取消挂载}if((play_record_flag !=0) && (key1_down_flag==0)) //开始播音{key1_down_flag=1;HAL_TIM_Base_Start_IT(&htim3);HAL_ADC_Start(&hadc1);//在外部 SD 卡挂载文件系统,文件系统挂载时会对 SD 卡初始化f_res = f_mount(&fs, "0:", 1); f_res = f_open(&fp, "0:ADC_DATA.txt", FA_OPEN_EXISTING | FA_READ);if(f_res==FR_OK){f_lseek(&fp, 44); //偏移文件起始位置44个字节,后面的就是所需要的数据fp_memory=f_size(&fp);printf("文件大小:%d\r\n",fp_memory);}}else if((play_record_flag ==0) && (key1_down_flag==0)){key1_down_flag=1;HAL_TIM_Base_Stop_IT(&htim3);HAL_ADC_Stop(&hadc1);f_close(&fp);f_mount(NULL,"0:",1); //取消挂载 }
}void FILE_RW_Test(void) //文件系统读写测试
{printf("挂载SD卡驱动\r\n");f_res=f_mount(&fs,"0:",1);f_unlink("0:ADC_DATA.txt");if(f_res == FR_OK){printf("挂载成功\r\n");f_res=f_open(&fp,"0:ADC_DATA.txt",FA_WRITE | FA_OPEN_ALWAYS);if(f_res == FR_OK){printf("文件打开成功\r\n");f_res=f_write(&fp,tab,10,&fp_rw_num);if(f_res == FR_OK){printf("写入成功\r\n实际写入字节数:%d\r\n",fp_rw_num);}}}f_close(&fp);f_res=f_open(&fp,"0:ADC_DATA.txt",FA_READ | FA_OPEN_ALWAYS);if(f_res == FR_OK){printf("文件打开成功\r\n");f_res=f_read(&fp,read_buff,5,&fp_rw_num);if(f_res == FR_OK){printf("读取成功\r\n");printf("%s\r\n",read_buff);}}
}void Audio_SD_Write(void)
{if(fp_wriet_flag == 1){fp_wriet_flag = 0;f_res=f_mount(&fs,"0:",1);if(f_res == FR_OK){printf("挂载成功\r\n");}elseprintf("挂载失败\r\n");f_res=f_open(&fp,"0:ADC_DATA.txt",FA_OPEN_ALWAYS | FA_WRITE);if(f_res == FR_OK)printf("文件打开成功\r\n");elseprintf("打开失败\r\n");f_lseek(&fp, f_size(&fp));//确保写词写入不会覆盖之前的数据f_res=f_write(&fp,val_data_buff,4096,&fp_rw_num);if(f_res == FR_OK){memset(val_data_buff,0,5000);printf("写入成功\r\n实际写入字节数:%d\r\n",fp_rw_num);}elseprintf("写入失败\r\n");}f_close(&fp);f_mount(NULL,"0:",1); //取消挂载
}void Audio_SD_Read(void)
{if(Time_Count>=fnum){ //当DAC输出的字节大于等于读取到的字节数Time_Count = 0;memset(DAC_Data,0,sizeof(DAC_Data)); //清空数组f_res=f_read(&fp,Read_Data,4096,(UINT*)&fnum);//读出4096个字节 memcpy(DAC_Data,Read_Data,fnum); //数组复制}if(f_eof(&fp)) //已经读取完了{f_lseek(&fp, 44); //跳过前面44个字节}
}
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
int wd4;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){if((sound_record_flag !=0) && (play_record_flag ==0)){val_data[val_count]=HAL_ADC_GetValue(&hadc1);val_count++;if(val_count == 2048){memcpy(val_data_buff,val_data,sizeof(val_data));val_count=0;fp_wriet_flag=1;}}if((sound_record_flag ==0) && (play_record_flag !=0)){Time_Count += DAC_F;wd4 = ((DAC_Data[Time_Count-1]-0x80)<<8)|(DAC_Data[Time_Count-2]); //将16位的音频数据转为12位。wd4 = 0xfff & (wd4>>4);HAL_DAC_SetValue(&hdac,DAC_CHANNEL_2,DAC_ALIGN_12B_R,wd4);//12位右对齐数据格式设置DAC值}}
}//main里面
HAL_DAC_Start(&hdac,DAC_CHANNEL_2);HAL_TIM_Base_Stop_IT(&htim3);HAL_ADC_Stop(&hadc1);HAL_Delay(1);// FILE_RW_Test(); //测试函数/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){KEY_Scanf();KEY_Handle();if((sound_record_flag !=0) && (play_record_flag ==0))Audio_SD_Write(); //录音else if((sound_record_flag ==0) && (play_record_flag !=0))Audio_SD_Read(); //播音}/* USER CODE END WHILE */
过程中遇到的问题
我在过程中遇到一个问题,就是在进行SD卡写数据时,一直循环写数据会出现写入失败的情况,我在网上找到几个可能的原因:
STM32读写SD卡
问题1:在循环进行写SD卡时,会出来写多次后出现写错误的情况。
问题原因1:
可能是通过APB2总线往FIFO装填数时,发生了中断,导致总线被占用或其他资源被占用,从而无法及时完成FIFO的装填,而后面从FIFO取数的频率又非常快,导致无法取到数。
解决方案1:
在调用f_write之前调用 __disable_irq() 接口关闭中断,写操作完成后调用 __enable_irq()接口启用中断:
__disable_irq();
if(FR_OK ==f_write())
{ }
else
{}
__disable_irq() ;
优点:简单
缺点:在写操作期间CPU无法响应中断
问题原因2:
SD卡写的时候出现了坏块,导致写不成功,在写失败时需要重试一下。
解决方案2:
重写bsp_driver_sd.c文件中的BSP_SD_WriteBlocks函数是_weak 函数,可以在自己的源文件myfile.c中重写该函数,当写发生错误后,清除错误标记,再重试几次即可,在我的单片机上,一般第一次重试即可成功
uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout)
{
int i;
for (i = 1; i <= TY_SDIO_RETRY_MAX; i ++)
{
if (HAL_SD_WriteBlocks(&hsd, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) == HAL_OK)
{
if (i > 1)
printf(“[INFO] SDIO writing succeeded: retry %d.\n”, i);
return (MSD_OK); // Succeeded
}
else
{
printf(“[ERROR] SDIO writing failure: retry %d, error code %#x, addr %#x, %u blocks.\n”,
i, hsd.ErrorCode, WriteAddr, NumOfBlocks);
HAL_SD_Abort(&hsd); //clear error flag
}
}
return (MSD_ERROR);
}
优点:上层应用软件不需要更改,也不需要禁用中断
缺点:频率较高时(我的是30MHZ)时,经常会发生写重试
问题原因3:
写速度太快,导致FIFO来不及装填,产生错误,需要降速。
解决方案3:
更改sdio.c中的MX_SDIO_SD_Init函数,将hsd.Init.ClockDiv设置为48分频,即SDIO的频率为主频除以48,我这里为72/48=1.5MHz。
优点:非常简单;无需禁用中断;无需对上层软件进行修改。
缺点:降低了写速度
我这里选择的时第3种,降低SD卡的读写速度
将这里的分频系数修改为48,速度就为72MHz/48.
注意事项:
在挂载SD卡,文件打开,完成一次读或写操作之后,要尽量关闭文件,取消SD卡挂载,下一次操作重新挂载和打开。
基于STM32的录音与播音相关推荐
- 【毕业设计】基于单片机的录音器设计与实现 - 物联网 嵌入式 stm32
文章目录 1 简介 2 绪论 2.1 课题背景与目的 3 系统设计 3.1 系统架构 3.2 硬件部分 3.3 软件部分 主程序流程 录放声音程序设计 LCD12864 程序设计 DS1302 程序设 ...
- stm32 带通滤波器_带通滤波 - 基于STM32芯片和TFT-LCD的便携式心电图仪设计
相关推荐 3月10日,纽约州州长安德鲁-库默(Andrew Cuomo)曾在上月宣布,纽约大都市圈的公共交通系... 发表于 2018-04-16 08:50 • 88次阅读 LCD驱动我们只需要写硬 ...
- 基于STM32的多功能MP3设计 毕业设计(论文)开题报告
中国计量学院 毕业设计(论文)开题报告 学生姓名:卢杰学 号:XXXXXXXXX 专 业:电子科学与技术 班 级:10电子1 设计(论文)题目: 基于STM32的多功能MP3设计 指导教师 ...
- 基于STM32的电子琴音乐播放器设计
基于STM32的电子琴/音乐播放器设计 文章目录 基于STM32的电子琴/音乐播放器设计 @[toc] 引言 第一章 总体设计 1.1 系统功能 1.2 主要技术性能指标 第二章 系统设计 2.1 系 ...
- 基于 STM32 的语音识别智能家居控制系统的设计(LD3320语音识别芯片+ESP8266 WIFI模块+DHT11温湿度采集+MQ系列 烟雾及可燃气体+蜂鸣器+步进电机模拟窗帘+OLED液晶显示+
## **基于 STM32 的语音识别智能家居控制系统的设计(LD3320语音识别芯片+ESP8266 WIFI模块(阿里云 或ONENET或局域网)+DHT11温湿度采集+MQ系列 烟雾及可燃气体+ ...
- 单片机毕业设计 基于stm32的病房呼叫系统
文章目录 1 简介 2 绪论 2.1 课题背景 3 系统设计 3.1 系统架构 3.2 主程序设计 3.3 语音模块程序设计 3.4 显示模块程序设计 3.5 键盘模块程序设计 3.6 无线传输模块程 ...
- 基于STM32实现孤立词语音识别系统
语音识别是机器通过识别和理解过程把人类的语音信号转变为相应文本或命令的技术,其根本目的是研究出一种具有听觉功能的机器.本设计研究孤立词语音识别系统及其在STM32嵌入式平台上的实现.识别流程是:预滤波 ...
- 基于STM32的微型电子琴设计
基于STM32的微型电子琴设计 第一章 总体设计 1.1 系统功能 1.2 主要技术性能指标 第二章硬件设计 2.1 整体硬件图 2.2 按键模块 2.3 扬声器模块 2.4 显示模块 2.5 主控模 ...
- 基于stm32物联网开发板(3)--SYN6288语音模块
基于stm32物联网开发板(3)–SYN6288语音模块 1.SYN6288语音模块展示示例 SYN6288语音模块 2.概述 SYN6288-A语音合成模块是一款性价比更高,效果更自然的一款中高 ...
- 基于STM32的多点温湿度无线传输检测及控制(基于单片机的蔬菜大棚温湿度智能控制系统)
基于STM32的多点温湿度无线传输检测及控制(基于单片机的蔬菜大棚温湿度智能控制系统) 一前言(含研究目的及意义) 1.研究目的 2.研究意义 二.研究工作的主要内容 三.理论创新成果 四.实践创新成 ...
最新文章
- 取文字_取一个好听的女孩名字
- GAITC 2020 演讲实录丨张立华:机器智能的发展现状
- SQL语句-exec执行
- go语言的channel特性
- 【研究任务】linux系统开机启动过程
- CentOS7搭建部署Ambari 2.6.2.0最新版(HDP-UTILS、HDP-GPL)大数据平台
- firewalld防火墙简介
- 商业初创公司网站单页模板
- Modbus​协议​深入​讲解_NI
- PHP foreach如何判断是数组最后一个元素
- Spring Cloud与微服务学习总结(5)——认证鉴权与API权限控制在微服务架构中的设计与实现(三)
- webpack遇见的坑:Please install 'webpack-cli' in addition to webpack itself to use the CLI.
- stream常用操作
- unity read files 从ini文件
- 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel
- 驾驶证/行驶证信息提取与识别
- Tips-不下载PS制作电子签名
- 云计算的三国演义!华为云、阿里云、腾讯云B端市场策略全解读
- Python Keras ValueError: Layer sequential expects 1 input(s), but it received 2 input tensors. 解决方法
- windows 和 Linux 查看IP属性(ipconfig,ifconfig)
热门文章
- m4s格式转换mp3_AnyMP4 MP3 Converter for Mac(音视频mp3格式转换工具)
- window自带字体
- 原生javascript的树形插件tree.js(纯原生js,无需引用其他js)
- ffmpeg webm 提取_使用ffmpeg转换webm格式
- 【童年回忆】4399造梦西游3,各版本CE教程汇总
- 使用MATLAB进行二次规划求解最优值
- 100行JS代码实现❤坦克大战js小游戏源码 HTML5坦克大战游戏代码(HTML+CSS+JavaScript )...
- 基于JAVA的KTV交易_Java写的KTV管理系统(Swing界面,含源码)
- 马尔科夫模型 Markov Model
- 【笔记】Python算法教程(1)