日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于STM32的录音与播音

發布時間:2023/12/10 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于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的录音与播音的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。