在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

使用STM32調試FMSDR模塊及解調FM電臺(3)

冬至子 ? 來源:硬木課堂 ? 作者:硬木課堂 ? 2023-07-06 11:00 ? 次閱讀

4. 使用MSI001解調8027發出的已知單音信號

4.1 輸出24Mhz和驗證SPI接口

  1. 硬件連接

    本例中我們添加MSI001相關的引腳也連接到STM32H750開發板。程序中操作的管腳如下描述:
    1.jpg

2. RCC時鐘輸出24MHz驅動Msi001

MSI001芯片需要輸入24MHz的時鐘作為參考信號,在這里使用專門的時鐘產生單元RCC產生24M的方波,提供給MSI001作為輸入參考信號。

使能Master clock output1后,配置PLL1Q輸出為48M,MCO1選擇時鐘源為PLL1Q,經過2分頻后,得到24M時鐘。

RCC產生24Mhz時鐘單元STM32CUBE配置如下:

3. 硬件SPI接口配置

芯片的控制接口是SPI協議,要使芯片正常工作,首先SPI接口的操作要正常。這里向MSI001芯片配置頻率為98.5Mhz,觀察配置前MSI001和配置后差分輸出管腳的波形變化。如果發生變化,說明SPI操作正常,芯片可以被控。這樣進行后續調試才有初步把握。

需要配置STM32H750的硬件SPI,然后發出控制字操作MSI001芯片,確認板卡和芯片正常工作。SPI工作速度設為3.75Mhz.

4. 編寫代碼

在main中使能RCC輸出,和寫MSI001寄存器

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_I2C2_Init();
  MX_DAC1_Init();
  MX_TIM6_Init();
  MX_SPI4_Init();
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);//tim2開啟pwm,輸出24Mhz
	for(i=0;i< SIN_ROM_LENGTH;i++)//生成sin表
	{
		sin_25_rom[i] = (uint16_t)(sin(2*3.14*i/(SIN_ROM_LENGTH))*1000 +2047);
	}
	HAL_TIM_Base_Start_IT(&htim6);//tim6開啟
	HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);//dac1的通道1開啟
	Qn8027_Init();	//qn8027初始化
	Msi001_Init();//msi初始化
  while (1)
  {
  }
}

添加MSI001驅動代碼

#include "msi001/msi001.h"

SPI_HandleTypeDef *msi001_spi = &hspi4; ///

uint32_t g_msi001_reg[7]={0};//msi寄存器配置

//msi001的spi發送三個字節,
HAL_StatusTypeDef Msi001_SPI_Transmit(uint32_t Data)
{
	HAL_StatusTypeDef errorcode = HAL_OK;
	uint8_t pData[4];

	pData[0] = (Data >>16)&0xFF;
	pData[1] = (Data >>8)&0xFF;
	pData[2] = (Data)&0xFF;

	errorcode = HAL_SPI_Transmit(msi001_spi,pData,3,10);
	return errorcode;
}

//msi001初始化,初始化成98.5M,改變寄存器參數配置不同頻率
HAL_StatusTypeDef Msi001_Init(void)
{
	uint32_t i=0;
	HAL_StatusTypeDef errorcode = HAL_OK;

//labview上位機配置為98.5m
	g_msi001_reg[0] = 0x043420;
	g_msi001_reg[1] = 0x00C141;
	g_msi001_reg[2] = 0x20BA12;
	g_msi001_reg[3] = 0x00FFF3;
	g_msi001_reg[4] = 0x000004;
	g_msi001_reg[5] = 0x28DF55;
	g_msi001_reg[6] = 0x200016;

	for(i=0;i< 6;i++)
	{
		errorcode = Msi001_SPI_Transmit(g_msi001_reg[i]);
	}
	return errorcode;
}

5. 測試MCO輸出的24MHz時鐘

如果方便,可以使用示波器測試stm32開發板的PA8(RCC_MCO_1)管腳,觀測有無24M的波形輸出。

6. MSI001寫測試

在前面程序中配置QN8027輸出的單音FM信號在98.5M上,下面我們把MSI001的接收頻點也配在98.5M,通過示波器查看MSI001芯片的IQ輸出的波形。

在main.c中,我們調用了SPI.c中的程序對SPI4進行初始化,配置SPI的時鐘,相位等;

在MSI001.c中,我們嘗試寫寄存器,使用示波器觀察MSI001的反應:

  1. keil中用debug單步調試,復位后,打斷點運行到初始化MSI001芯片前。

  1. 運行到下一行,配置寄存器0為0x143420后,示波器的表現如下:

  1. 再運行一行,配置寄存器0為0x243420后,示波器的表現如下:

  1. 再運行一行,配置寄存器0為0x043420后,示波器的表現如下:

如果IQ輸出能夠跟隨我們寫入的寄存器動作,這說明SPI時序正確,硬件也是好的,這時我們就可以進行下一步操作了。

注意,SPI時序寫入這一步看上去雖然簡單,卻也是最經常出問題的步驟。如果遇到MSI001沒有反應,建議用如下方法排查:

  1. 電源測試:MSI001供電是否正常;
  2. IO通斷測試:使用IO輸出高低電平,通過測量確定PCB焊接正確,且插對了孔位;
  3. SPI時序測試:使用示波器或邏輯分析儀捕獲發出的SPI時序,判斷是否SPI配置寄存器有錯誤;FPGA寫的SPI程序,則要特別留意是否有代碼bug。
  4. 如果管腳上的SPI時序正確,但MSI001如果沒有應答,觀察是否有虛焊等情況(開發板發貨前經過測試,基本上可以排除電源和8027的焊接問題)
  5. 為減少MSI001死掉的幾率,使能STM32或FPGA管腳內部的下拉或上拉電阻,SPI時序正常的情況下沒有反應,可以全板掉電重啟試試。

如果沒有示波器,可以使用STM32內部ADC采集后通過UART傳到上位機觀察波形,請查看下一節內容。

4.2 ADC采集和UARTPlot

1. 硬件連接

本例中我們使用CMSIS-DAP上自帶的UART2USB功能,把ADC采集到的數據發到電腦,通過UARTPlot軟件觀察采集的波形。程序中操作的管腳如下描述:

1.jpg

1.jpg

2. 配置ADC1/2同步差分輸入DMA采集

STM32處理數據流的能力遠不如FPGA,要實現實時信號處理,在STM32中我們需要使用雙緩沖的方法,即準備兩個數據段,采集A段數據的時候處理B數據段,采集B數據段的時候處理A數據段,這樣才能確保波形的連續實時處理。

本例中我們要實現的功能是定時器TIM1輸出TRG信號觸發ADC1和ADC2,實現500KSPS采樣率的DMA雙緩沖采集,采集完后通過UART發送IQ數據給上位機,通過上位機軟件觀察波形。

實現思路如下:將DMA采集輸出長度設為2000個點,采集完前半部分1000個點后,進入半回調函數HAL_ADC_ConvHalfCpltCallback中,標志位置1;后半部分1000個點采集完成后,進入HAL_ADC_ConvCpltCallback,標志位置2,停止DMA采集;在主程序中如果標志位等于2,就將IQ數據發送到PC上位機顯示,再開啟DMA進行下一幀數據采集。這樣確保我們看到的波形正確后,就可以進入下一節,進行后面的FM解調處理。

實現思路框圖如下:

在while循環中,一直進行標志位判斷,標志位是2時,發送數據。回調函數和半回調函數都是通過DMA1_Stream0_IRQHandler中斷進入。

具體ADC同步DMA采集介紹,可以回顧基礎實驗 “實驗二十四 ADC定時器觸發配合DMA雙緩沖實現實時采集”

在程序中,添加了串口打印函數printf用于發送數據到上位機,可以回顧基礎實驗 “實驗十六 串口通信

3. 程序解讀

while (1)
  {

			if(g_adc1_dma_complete_flag == 2) //采集數據完成
			{
				for(i=0;i< ADC_DATA_LENGTH;i++)
				{
					adc1_I_voltage[i] = ((float)3.3*((g_adc1_dma_data1[i])&0x0000ffff))/65536; //轉換碼值為電壓值
					adc2_Q_voltage[i] =	((float)3.3*((g_adc1_dma_data1[i] >>16)&0x0000ffff))/65536; //轉換碼值為電壓值
				}

				//Send I Channel Data to PC
				for(i=0;i< ADC_DATA_LENGTH;i++)
				{
					printf("%f\\r\\n", adc1_I_voltage[i]); 
		        }
				HAL_Delay(100);//Delay

        //Send Q Channel Data to PC			
				for(i=0;i< ADC_DATA_LENGTH;i++)
				{
					printf("%f\\r\\n",adc2_Q_voltage[i]); 
				}
				HAL_Delay(100);//Delay

				//Restart DMA
				g_adc1_dma_complete_flag = 0;		
				memset(&g_adc1_dma_data1[0],0,ADC_DATA_LENGTH);				HAL_ADCEx_MultiModeStart_DMA(&hadc1,g_adc1_dma_data1,ADC_DATA_LENGTH);
			}
			/* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

4. 使用UART將波形數據發送給UARTPlot (pyserial_display) 軟件

使用串口數據接收軟件“pyserial_display.exe”的步驟:

  • 選擇CMSIS-DAP對應的串口號
  • 設定串口波特率115200
  • 數據位8,校驗位N,停止位1
  • 波形長度2000,浮點類型,雙通道模式
  • CH1是I通道波形,CH2是Q通道波形。
  • 點擊開始采集,等待下位機數據。

UartPlot的使用注意事項:

  • 確保上位機設置的UART參數(波特率、數據位、校驗位、停止位)與大拇指開發板中的程序設定一致。
  • 檢查波形長度,通道數,顯示數據格式是否和大拇指開發板中程序一致;
  • 由于本軟件沒有使用幀頭等傳輸協議,在使用軟件時, 先在UARTPlot 上位機界面上點擊開始采集,然后在大拇指開發板上啟動數據傳輸 ,確保上位機軟件捕獲到數組的起始點,如果沒有遵循上述啟動流程,會出現波形截斷現象。停止上位機并重復上述流程即可修復。
  • H750例程使用的是板載DAP調試器的UART2USB功能,波特率設定為115200,上位機界面選擇USB串行設備。注意:DAP調試器在Debug模式下同時使用UART2USB功能傳輸數據可能導致調試器死機(死機后表現為DAP連不上芯片,Keil報No Debug Device Found),此時按住H750板上的BOOT0按鈕不放,重新拔插USB后下載已知可運行程序可以解決。

一幀數據顯示

按住鼠標左鍵,可以上下左右移動波形,按住鼠標右鍵,可以放大或者縮小X軸或Y軸:

點擊CH1或者CH2可以關閉或打開指定通道的波形顯示

如果鼠標不能用,在波形顯示界面點擊鼠標右鍵,可以選擇是否在X軸或Y軸啟用鼠標:

在Plot Options里,可以對波形做FFT等處理

在Export里可以導出波形數據為JPG,或EXCEL文件

4.3 FM解調算法

1. FM解調算法回顧

求解頻率,FM解調

在利用相位差分計算瞬時頻率f(n)時,由于計算相位要用到除法和反正切運算,這對于非專用數字處理器來說是較復雜的,在用軟件實現時,也可用下面的方法來計算瞬時頻率f(n)

**對于FM信號,其振幅近似恒定,可以設定 **,則

這就是利用XI(n)和XQ(n)計算f(n)的近似公式。這種方法只有乘法和減法,計算簡便。也是開發板例程中用到的方法。

2. 編寫代碼

if(g_adc1_dma_complete_flag == 2)
			{
				for(i=0;i< ADC_DATA_LENGTH;i++)
				{
					adc1_I_voltage[i] = ((float)3.3*((g_adc1_dma_data1[i])&0x0000ffff))/65536;
					adc2_Q_voltage[i] =	((float)3.3*((g_adc1_dma_data1[i] >>16)&0x0000ffff))/65536;
				}

				// Send I Channel Data to PC
				for(i=0;i< ADC_DATA_LENGTH;i++)
				{
					printf("%f\\r\\n",adc1_I_voltage[i]);
				}
				HAL_Delay(200);//Delay

				// FM demodulate
				for(i=0;i< ADC_DATA_LENGTH;i++)
				{
					if (i==ADC_DATA_LENGTH-1)
					{
					adc12_fm_out[i] = adc12_fm_out[i-1];
					}
					else
					{
					adc12_fm_out[i] = (adc1_I_voltage[i]*adc2_Q_voltage[i+1] - adc1_I_voltage[i+1]*adc2_Q_voltage[i])*1;
					}	
				}

				// Send demodulated data to PC
				for(i=0;i< ADC_DATA_LENGTH;i++)
				{
					printf("%f\\r\\n",adc12_fm_out[i]);
				}
					HAL_Delay(200);//Delay

				//Restart DMA
				g_adc1_dma_complete_flag = 0;		
				memset(&g_adc1_dma_data1[0],0,ADC_DATA_LENGTH);				HAL_ADCEx_MultiModeStart_DMA(&hadc1,g_adc1_dma_data1,ADC_DATA_LENGTH);

3. FM解調測試

CH1是I通道波形,CH2是解調后的波形。

解調后波形(紅色)可以看出1KHz的成分,需要后續進行濾波處理。

4.3 實時信號抽取和DAC輸出

1. 硬件連接

程序中操作的管腳如下描述:

1.jpg

2. 實時信號采集和500K信號抽取

STM32和FPGA不同的地方在于,MCU處理數據流的能力較弱,對于500KSPS的連續FIR濾波軟件開銷較大,因此在STM32程序中我們先抽取降速后再進行FIR濾波(下一節介紹),降低系統的運算量。

本例中我們要實現的功能為:定時器TIM1輸出TRG信號觸發500KSPS采樣率的ADC1和ADC2,實現DMA雙緩沖采集,進行實時采集->解調->每20個數據平均為1個數據->通過TIM6觸發的刷新率為25KSPS的DAC發出。最后通過示波器觀察DAC管腳波形。

定時器觸發ADC做DMA雙緩沖數據傳輸的實現思路是:

  1. 將DMA采集輸出長度設為2000個點,DMA配置為循環模式一直進行自動采集;
  2. 采集完成前1000個點,調用半回調函數HAL_ADC_ConvHalfCpltCallback,進行解調,20次平均,如果平均后數據達到12500個后,標志位置1,while(1)中進行前半段12500個數據處理(DAC幅度變換,更新DAC數組);
  3. 采集完成后1000個點,調用回調函數HAL_ADC_ConvCpltCallback,進行解調,20次平均,平均后數據如果達到25000個后,標志位置2,在While(1)中進行后半段12500個數據處理(DAC幅度變換,更新DAC數組)。

實現思路框圖如下:

具體ADC同步DMA采集介紹,可以回顧基礎實驗 “實驗二十四 ADC定時器觸發配合DMA雙緩沖實現實時采集”:

3. While循環處理

在while循環中,一直進行標志位判斷,標志位是1時,處理前半部分12500個數據處理,標志位是2時,處理后半部分12500個數據處理。完成回調函數和半完成回調函數都是通過DMA1_Stream0_IRQHandler中斷進入。

4. 編寫代碼

在main中初始化接口,配置芯片,while循環中處理數據

int main(void)
{
uint32_t i=0;
float iq_temp=0;//臨時存儲
uint32_t dac2_start_flag=0;//第一次dac開啟標志  HAL_Init();
  SystemClock_Config();
PeriphCommonClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_I2C2_Init();
  MX_DAC1_Init();
  MX_TIM6_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_TIM1_Init();
  MX_SPI4_Init();
MX_UART4_Init(); 
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);//tim2開啟pwm,輸出24Mhz
for(i=0;i< SIN_ROM_LENGTH;i++)//生成sin表
	{
		sin_25_rom[i] = (uint16_t)(sin(2*3.14*i/(SIN_ROM_LENGTH))*1000 +2047);
	}
	HAL_TIM_Base_Start_IT(&htim6);//tim6開啟
	HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);//dac1的通道1開啟
	Qn8027_Init();	//qn8027初始化
	Msi001_Init();//msi初始化

	HAL_Delay(100);
	HAL_TIM_Base_Start_IT(&htim1);//tim1開啟
	HAL_ADCEx_MultiModeStart_DMA(&hadc1,g_adc1_dma_data1,ADC_DATA_LENGTH);//ADC的dma開始采集
  while (1)
  {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			if(g_adc1_dma_complete_flag==1)//采集完前12500個數據后,進入這個部分
			{
				for(i=0;i< ADC_FIR_DATA_LENGTH/2;i++)//將fir濾波器輸出值幅度縮小范圍,再將直流偏置調整到1.65v,再計算出DAC對應的碼值
				{					
					iq_fir_out[i] = iq_fir_in[i]*0.9;
					iq_temp = iq_fir_out[i]+1.65;
					iq_temp = iq_temp/3.3;
					iq_temp = iq_temp * 4095;
					audio_out_dac[i] = ((uint16_t)iq_temp)&0x0fff;
				}

				if(dac2_start_flag ==0)	//第一次進入dac開啟,只需第一次開啟
				{					
					HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);//dac1的通道2開啟
					dac_phase=0;
					dac2_start_flag=1;
				}
				g_adc1_dma_complete_flag=0;
			}
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////
			
			if(g_adc1_dma_complete_flag==2)//采集完后12500個數據后,進入這個部分
			{
				for(i=ADC_FIR_DATA_LENGTH/2;i< ADC_FIR_DATA_LENGTH;i++)//將fir濾波器輸出值幅度縮小范圍,再將直流偏置調整到1.65v,再計算出DAC對應的碼值
				{					
					iq_fir_out[i] = iq_fir_in[i]*0.9;
					iq_temp = iq_fir_out[i]+1.65;
					iq_temp = iq_temp/3.3;
					iq_temp = iq_temp * 4095;
					audio_out_dac[i] = ((uint16_t)iq_temp)&0x0fff;
				}
				g_adc1_dma_complete_flag=0;
			}  }
}

5. ADC中斷回調函數

采集完成前1000個點,調用半回調函數HAL_ADC_ConvHalfCpltCallback,解調,20次平均,如果平均后數據達到12500個后,標志位置1。

采集完成后1000個點,調用回調函數HAL_ADC_ConvCpltCallback,解調,20次平均,平均后數據如果達到12500個后,標志位置2

驅動代碼:

#define ADC_DATA_LENGTH  2000   //定義采集數據長度為2000
uint32_t g_adc1_dma_data1[ADC_DATA_LENGTH];//定義adc1采集數據存放數組
uint8_t g_adc1_dma_complete_flag = 0;	//adc1數據dma采集完成標志,在dma采集完成回調函數設置
float adc1_I_voltage[ADC_DATA_LENGTH];//定義I采集數據存放數組
float adc2_Q_voltage[ADC_DATA_LENGTH];//定義Q采集數據存放數組
float adc12_iq_out[ADC_DATA_LENGTH];//定義out存放數組

#define ADC_FIR_DATA_LENGTH  25000   //定義采集數據長度為1000
uint16_t audio_out_dac[ADC_FIR_DATA_LENGTH] = {0};//輸出dac
float iq_fir_in[ADC_FIR_DATA_LENGTH];//濾波前數據
float iq_fir_out[ADC_FIR_DATA_LENGTH];//濾波后數據

uint32_t iq_fir_in_cnt =0; //平均后,存放數據的下標


//ADC回調函數,ADC采集完成后進入回調函數
//adc端口
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{	
	uint32_t i=0,j=0;
	if(hadc- >Instance==ADC1)
		{
		for(i=ADC_DATA_LENGTH/2;i< ADC_DATA_LENGTH;i++)
		{
			adc1_I_voltage[i] = ((float)3.3*((g_adc1_dma_data1[i])&0x0000ffff))/65536;//采集到的值轉換為電壓
			adc2_Q_voltage[i] =	((float)3.3*((g_adc1_dma_data1[i] >>16)&0x0000ffff))/65536;//采集到的值轉換為電壓
		}

		//IQ通道解調
			for(i=ADC_DATA_LENGTH/2;i< ADC_DATA_LENGTH;i++)
		{			
			if (i==ADC_DATA_LENGTH-1)
					{
					adc12_iq_out[i] = adc12_iq_out[i-1];
					}
					else
					{
					adc12_iq_out[i] = (adc1_I_voltage[i]*adc2_Q_voltage[i+1] - adc1_I_voltage[i+1]*adc2_Q_voltage[i])*1;
					}
			}
		//20倍抽取:每20個點求平均
		for(i=ADC_DATA_LENGTH/2/20;i< ADC_DATA_LENGTH/20;i++)
		{
			iq_fir_in[iq_fir_in_cnt] =0;
			for(j=0;j< 20;j++)
			{
				iq_fir_in[iq_fir_in_cnt] += adc12_iq_out[20*i + j]; 
			}
			iq_fir_in[iq_fir_in_cnt] = iq_fir_in[iq_fir_in_cnt]/20;//求平均
			iq_fir_in_cnt++;
			if(iq_fir_in_cnt==ADC_FIR_DATA_LENGTH/2)
			{
				g_adc1_dma_complete_flag = 1;//前半段采集完成標志
			}
			if(iq_fir_in_cnt==ADC_FIR_DATA_LENGTH)
			{
				g_adc1_dma_complete_flag = 2;//后半段采集完成標志
				iq_fir_in_cnt=0;
			}
		}

	}
}

//ADC半傳輸回調函數,ADC采集完成一半后后進入回調函數
//adc端口號
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
	uint32_t i=0,j=0;
	if(hadc- >Instance==ADC1)
	{

		for(i=0;i< ADC_DATA_LENGTH/2;i++)
		{
			adc1_I_voltage[i] = ((float)3.3*((g_adc1_dma_data1[i])&0x0000ffff))/65536;//采集到的值轉換為電壓
			adc2_Q_voltage[i] =	((float)3.3*((g_adc1_dma_data1[i] >>16)&0x0000ffff))/65536;//采集到的值轉換為電壓
		}

		//IQ通道解調
			for(i=0;i< ADC_DATA_LENGTH/2;i++)
		{
				  if (i==ADC_DATA_LENGTH/2-1)
					{
					adc12_iq_out[i] = adc12_iq_out[i-1];					}
					else
					{
					adc12_iq_out[i] = (adc1_I_voltage[i]*adc2_Q_voltage[i+1] - adc1_I_voltage[i+1]*adc2_Q_voltage[i])*1;
					}	
		}
		//20倍抽取,20次求平均
		for(i=0;i< ADC_DATA_LENGTH/2/20;i++)
		{
			iq_fir_in[iq_fir_in_cnt] =0;
			for(j=0;j< 20;j++)
			{
				iq_fir_in[iq_fir_in_cnt] += adc12_iq_out[20*i + j]; 
			}
			iq_fir_in[iq_fir_in_cnt] = iq_fir_in[iq_fir_in_cnt]/20;//求平均
			iq_fir_in_cnt++;
			if(iq_fir_in_cnt==ADC_FIR_DATA_LENGTH/2)
			{
				g_adc1_dma_complete_flag = 1;//前半段采集完成標志
			}
			if(iq_fir_in_cnt==ADC_FIR_DATA_LENGTH)
			{
				g_adc1_dma_complete_flag = 2;//后半段采集完成標志
				iq_fir_in_cnt=0;
			}
		}
	}
}

6. DAC中斷輸出

STM32H750的DAC是雙通道,其中一個通道用于在PA4管腳上輸出1k正弦波送給8027調制,另一個通道用于在PA5管腳上輸出經過FM解調/平均/DAC變換后數據

//tim6中斷處理函數,每進入一次中斷,輸出新碼表值
void  TIM6_DAC_IRQHandler(void)
{

		__HAL_TIM_CLEAR_IT(&htim6, TIM_IT_UPDATE);//清除tim6的事件更新標志

		if(dac_phase >= ADC_FIR_DATA_LENGTH)
		{
			dac_phase = 0;
		}
				HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_2,DAC_ALIGN_12B_R,audio_out_dac[dac_phase]);//通道2輸出
		dac_phase++;	

		if(sin_phase >= SIN_ROM_LENGTH)
		{
			sin_phase = 0;
		}
		HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,DAC_ALIGN_12B_R,sin_25_rom[sin_phase]);//通道1輸出
		sin_phase++;
}

7. 中斷優先級

需要注意中斷優先級,否則DAC輸出波形會出錯。DAC優先級必須最高。

DAC(TIM6)>DMA/ADC>TIM1.

8. PA5管腳輸出

下載編譯好的程序到開發板。

示波器CH1是開發板PA5管腳輸出的解調后的1KHz波形,CH2是經過板上硬件濾波器后的1KHz。

可以看到解調后輸出波形(CH1)很多毛刺,通過模擬濾波器后,波形得到改善(CH2)。

在下一節中,我們將在MCU里給解調、抽取之后的數據使用Fir數字濾波器,這樣DAC發出波形的時候就會得到改善。

通過喇叭可以聽到聲音。

4.4 FIR濾波器設計

**本例中是在前面3.3.3例程基礎上,添加FIR濾波器實現,設計帶通濾波器,高截止頻率為3kHz,低截止頻率為100Hz。將濾波后的信號通過PA5管腳輸出。通過示波器觀察處理后DAC管腳PA5波形. **

1. 硬件連接同4.3.1節

2. 濾波器介紹

線性時不變的數字濾波器包括無限長脈沖響應濾波器(IIR濾波器)和有限長脈沖響應濾波器(FIR濾波器)兩種。這兩種濾波器的系統函數可以統一以Z變換表示為:

當M≥1時,M就是IIR濾波器階數,表示系統反饋環的個數,由于反饋的存在,IIR濾波器的脈沖響應為無限長,因此得名,若A(z) = 1,則系統的脈沖響應的長度為N+1,故稱為FIR濾波器。

IIR濾波器的優點在于,其設計可以直接利用模擬濾波器設計的成果,因為模擬濾波器本身就是無限長沖激響應的。通常IIR濾波器設計的過程如下:首先根據濾波器參數要求設計對應的模擬濾波器(如巴特沃斯濾波器、切比雪夫濾波器等等),然后通過映射(如脈沖響應不變法、雙線性映射等等)將模擬濾波器變換為數字濾波器,從而決定IIR濾波器的參數。IIR濾波器的重大缺點在于,由于存在反饋其穩定性不能得到保證。另外,反饋還使IIR濾波器的數字運算可能溢出。

FIR濾波器最重要的優點就是由于不存在系統極點,FIR濾波器是絕對穩定的系統。FIR濾波器還確保了線性相位,這在信號處理中也非常重要。此外,由于不需要反饋,FIR濾波器的實現也比IIR濾波器簡單。FIR濾波器的缺點在于它的性能不如同樣階數的IIR濾波器,不過由于數字計算硬件的飛速發展,這一點已經不成為問題。再加上引入計算機輔助設計,FIR濾波器的設計也得到極大的簡化。基于上述原因,FIR濾波器比IIR濾波器的應用更廣。

具體DSP濾波器介紹,可以回顧DSP實驗 “實驗十三 DSP濾波器基礎知識”, “實驗十四 DSP FIR有限沖擊濾波器設計”, “實驗十五 DSP FIR濾波器的matlab設計(低通,高通,帶通,帶阻)”。

3. Matlab生成FIR濾波器系數

下面我們講解下如何通過Matlab的filterDesigner工具生成C頭文件,也就是生成濾波器系數。打開應用程序“Filter Design & Analysis”,

**filterDesigner界面打開效果如下: **

這里重點介紹設置后相應參數后如何生成濾波器系數。

**降采樣率后進入FIR濾波器的采樣率為25KHz,現設計一個帶通濾波器,高截止頻率3000Hz,低截止頻率100Hz,采用函數fir1進行設計(注意這個函數是基于窗口的方法設計FIR濾波,默認是hamming窗),濾波器階數設置為28。filterDesigner的配置如下: **

點擊Design Filter按鈕以后就生成了所需的濾波器系數,生成濾波器系數以后點擊filterDesigner界面上的菜單Targets->Generate C header** ,選擇單精度浮點輸出,打開后顯示如下界面: **

然后點擊Generate,生成如下界面:

再點擊保存,并打開fir_bandpass.h文件,可以看到生成的系數,一共有29個系數:

/*
 * Discrete-Time FIR Filter (real)
 * -------------------------------
 * Filter Structure  : Direct-Form FIR
 * Filter Length     : 29
 * Stable            : Yes
 * Linear Phase      : Yes (Type 1)
 */

const int BL = 29;
const real32_T B[29] = {
-0.002292175079,-0.001557604875,0.0002387679269, 0.003326080972, 0.005709242076,
   0.003126602387,-0.007875042036,  -0.0252452381, -0.03892513365, -0.03386418894,
   0.001657098066,  0.06692832708,   0.1452077776,   0.2092580795,   0.2339902967,  0.2092580795,   0.1452077776,  0.06692832708, 0.001657098066, -0.03386418894,  -0.03892513365,  -0.0252452381,-0.007875042036, 0.003126602387, 0.005709242076, 0.003326080972,0.0002387679269,-0.001557604875,-0.002292175079
};

4. KEIL添加庫文件

因需要用到DSP庫,在工程中添加如下源文件。

浮點運算使能

添加DSP預定義結構

添加文件路徑

5. 編寫代碼

在main中初始化接口,配置芯片,while循環中處理數據。

在前面工程基礎上添加濾波器初始化函數****arm_fir_f32_bp_init();

在while循環中添加fir濾波函數****arm_fir_f32_bp。

數據達到12500個后,處理前部分12500數據的fir濾波。

數據達到25000個后,處理后部分12500數據的fir濾波。

int main(void)
{
uint32_t i=0;
float iq_temp=0;//臨時存儲
uint32_t dac2_start_flag=0;//第一次dac開啟標志  HAL_Init();
  SystemClock_Config();
PeriphCommonClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_I2C2_Init();
  MX_DAC1_Init();
  MX_TIM6_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_TIM1_Init();
  MX_SPI4_Init();
MX_UART4_Init(); 
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);//tim2開啟pwm,輸出24Mhz
	for(i=0;i< SIN_ROM_LENGTH;i++)//生成sin表
	{
		sin_25_rom[i] = (uint16_t)(sin(2*3.14*i/(SIN_ROM_LENGTH))*1000 +2047);
	}
arm_fir_f32_bp_init();//帶通濾波器初始化
HAL_TIM_Base_Start_IT(&htim6);//tim6開啟
HAL_DAC_Start(&hdac1,DAC_CHANNEL_1);//dac1的通道1開啟
Qn8027_Init();	//qn8027初始化
Msi001_Init();//msi初始化

HAL_Delay(100);
HAL_TIM_Base_Start_IT(&htim1);//tim1開啟
HAL_ADCEx_MultiModeStart_DMA(&hadc1,g_adc1_dma_data1,ADC_DATA_LENGTH);//ADC的dma開始采集
  while (1)
  {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			if(g_adc1_dma_complete_flag==1)//采集完前12500個數據后,進入這個部分
			{
arm_fir_f32_bp(&iq_fir_in[0],&iq_fir_out[0]);//fir濾波器
				for(i=0;i< ADC_FIR_DATA_LENGTH/2;i++)//將fir濾波器輸出值幅度縮小范圍,再將直流偏置調整到1.65v,再計算出DAC對應的碼值
				{					
					iq_fir_out[i] = iq_fir_in[i]*0.9;
					iq_temp = iq_fir_out[i]+1.65;
					iq_temp = iq_temp/3.3;
					iq_temp = iq_temp * 4095;
					audio_out_dac[i] = ((uint16_t)iq_temp)&0x0fff;
				}

				if(dac2_start_flag ==0)	//第一次進入dac開啟,只需第一次開啟
				{					
					HAL_DAC_Start(&hdac1,DAC_CHANNEL_2);//dac1的通道2開啟
					dac_phase=0;
					dac2_start_flag=1;
				}
				g_adc1_dma_complete_flag=0;
			}
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////
			
			if(g_adc1_dma_complete_flag==2)//采集完后12500個數據后,進入這個部分
			{
			arm_fir_f32_bp(&iq_fir_in[ADC_FIR_DATA_LENGTH/2],&iq_fir_out[ADC_FIR_DATA_LENGTH/2]);//fir濾波器
					for(i=ADC_FIR_DATA_LENGTH/2;i< ADC_FIR_DATA_LENGTH;i++)//將fir濾波器輸出值幅度縮小范圍,再將直流偏置調整到1.65v,再計算出DAC對應的碼值
				{					
					iq_fir_out[i] = iq_fir_in[i]*0.9;
					iq_temp = iq_fir_out[i]+1.65;
					iq_temp = iq_temp/3.3;
					iq_temp = iq_temp * 4095;
					audio_out_dac[i] = ((uint16_t)iq_temp)&0x0fff;
				}
				g_adc1_dma_complete_flag=0;
			}  }
}

6. FIR實現

FIR濾波實現先初始化濾波參數,在進行數據濾波。

具體FIR濾波器實現講解,可以回顧DSP實驗 “實驗十六 STM32H7的FIR低通濾波器實現”, “實驗十七 STM32H7的FIR高通濾波器實現”。

實現代碼:

#define TEST_LENGTH_SAMPLES  12500    /* 采樣點數 */
#define BLOCK_SIZE           1    	 /* 調用一次arm_fir_f32處理的采樣點個數 */
#define NUM_TAPS             29      /* 濾波器系數個數 */

uint32_t blockSize = BLOCK_SIZE;
uint32_t numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;            /* 需要調用arm_fir_f32的次數 */

//static float32_t testInput_f32_50Hz_200Hz[TEST_LENGTH_SAMPLES]; /* 采樣點 */
//static float32_t testOutput[TEST_LENGTH_SAMPLES];               /* 濾波后的輸出 */
static float32_t firStateF32[BLOCK_SIZE + NUM_TAPS - 1];        /* 狀態緩存,大小numTaps + blockSize - 1*/


/* 濾波器系數 通過fadtool獲取*/
const float32_t firCoeffs32BP[NUM_TAPS] = {
	 -0.002292175079,-0.001557604875,0.0002387679269, 0.003326080972, 0.005709242076,
   0.003126602387,-0.007875042036,  -0.0252452381, -0.03892513365, -0.03386418894,
   0.001657098066,  0.06692832708,   0.1452077776,   0.2092580795,   0.2339902967,
     0.2092580795,   0.1452077776,  0.06692832708, 0.001657098066, -0.03386418894,
   -0.03892513365,  -0.0252452381,-0.007875042036, 0.003126602387, 0.005709242076,
   0.003326080972,0.0002387679269,-0.001557604875,-0.002292175079
};

/*
*********************************************************************************************************
*	函 數 名: arm_fir_f32_bp_ini
*	功能說明: 調用函數arm_fir_init_f32初始化S
*	形    參:無
*	返 回 值: 無
*********************************************************************************************************
*/

arm_fir_instance_f32 S;//初始化結構值,算fir時需要用到

void arm_fir_f32_bp_init(void)
{
	/* 初始化結構體S */
	arm_fir_init_f32(&S,                            
					 NUM_TAPS, 
	                (float32_t *)&firCoeffs32BP[0], 
	                 &firStateF32[0], 
	                 blockSize);
}

/*
*********************************************************************************************************
*	函 數 名: arm_fir_f32_bp
*	功能說明: 調用函數arm_fir_f32_bp實現帶通濾波器
*	形    參:無
*	返 回 值: 無
********************************************************************************************************
*/
void arm_fir_f32_bp(float32_t  *inputF32,float32_t *outputF32)
{
	uint32_t i;

	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_3,GPIO_PIN_SET);
	/* 實現FIR濾波,這里每次處理1個點 */
	for(i=0; i < numBlocks; i++)
	{				
		arm_fir_f32(&S, inputF32 + (i * blockSize),  outputF32 + (i * blockSize),  blockSize);
	}
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_3,GPIO_PIN_RESET);

}

7. PA5管腳輸出

下載編譯好的程序到開發板。

示波器CH1是開發板PA5管腳輸出波形,CH2是PB1管腳波形。

可以看到FIR濾波輸出波形CH1是個很干凈的正弦波(臺階為采樣率,可以數出一個周期25個點),頻率為1kHz。CH2是通過硬件濾波器后的波形。

對比3.3.3節的波形,DAC輸出的噪聲明顯變小。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 示波器
    +關注

    關注

    113

    文章

    6288

    瀏覽量

    185943
  • RCC
    RCC
    +關注

    關注

    0

    文章

    93

    瀏覽量

    27101
  • 上位機
    +關注

    關注

    27

    文章

    945

    瀏覽量

    55034
  • PCB焊接
    +關注

    關注

    0

    文章

    14

    瀏覽量

    8037
  • 調試器
    +關注

    關注

    1

    文章

    306

    瀏覽量

    23847
收藏 人收藏

    評論

    相關推薦

    使用STM32調試FMSDR模塊解調FM電臺(1)

    示波器:本實驗我們使用硬木課堂的口袋儀器產品進行測量
    的頭像 發表于 10-13 15:52 ?2800次閱讀
    使用<b class='flag-5'>STM32</b><b class='flag-5'>調試</b><b class='flag-5'>FMSDR</b><b class='flag-5'>模塊</b>及<b class='flag-5'>解調</b><b class='flag-5'>FM</b><b class='flag-5'>電臺</b>(1)

    使用STM32調試FMSDR模塊解調FM電臺3

    QN8027芯片需要輸入24MHz的時鐘作為參考信號,在這里通過STM32H750的TIMER2產生24M的方波,提供給QN8027作為輸入參考信號。
    的頭像 發表于 10-13 16:04 ?5269次閱讀
    使用<b class='flag-5'>STM32</b><b class='flag-5'>調試</b><b class='flag-5'>FMSDR</b><b class='flag-5'>模塊</b>及<b class='flag-5'>解調</b><b class='flag-5'>FM</b><b class='flag-5'>電臺</b>(<b class='flag-5'>3</b>)

    教你最簡單做一個隨身FM電臺!X各種使用方法#電臺

    電臺FM
    Dimsmary
    發布于 :2022年07月12日 14:29:14

    FM解調器電路

    幾種FM解調器電路
    發表于 04-21 11:49 ?2002次閱讀
    <b class='flag-5'>FM</b><b class='flag-5'>解調</b>器電路

    哈爾濱文藝電臺fm頻道列表

    哈爾濱文藝電臺fm頻道列表 [FM]CH01=09980,黑龍江交通廣播CH02=10450,黑龍江生活廣播 CH03=09250,哈爾濱經濟電臺 CH04=10210,黑龍江婦女兒
    發表于 05-15 23:12 ?3907次閱讀

    FM解調器A電路

    FM解調器A電路
    發表于 03-21 19:00 ?875次閱讀
    <b class='flag-5'>FM</b><b class='flag-5'>解調</b>器A電路

    線性FM解調器電路

    線性FM解調器電路
    發表于 03-21 19:14 ?1014次閱讀
    線性<b class='flag-5'>FM</b><b class='flag-5'>解調</b>器電路

    FM解調電路圖

    FM解調電路圖
    發表于 07-15 16:47 ?1157次閱讀
    <b class='flag-5'>FM</b><b class='flag-5'>解調</b>電路圖

    FM解調

    FM解調器 LM311
    發表于 09-15 10:28 ?1539次閱讀
    <b class='flag-5'>FM</b><b class='flag-5'>解調</b>器

    455KHz FM解調

    455KHz FM解調
    發表于 09-15 11:04 ?1177次閱讀
    455KHz <b class='flag-5'>FM</b><b class='flag-5'>解調</b>器

    FM調制/解調電路的設計方案分析

    ,我們得到的載波信號的電壓大于3V,最大頻率偏移5KHz,解調電路輸出的FM調制信號的電壓大于200mV可以看出我們的具體設計符合設計指標。 關鍵詞:鎖相環、調制、解調、濾波器 一、概
    發表于 10-27 17:04 ?57次下載
    <b class='flag-5'>FM</b>調制/<b class='flag-5'>解調</b>電路的設計方案分析

    使用STM32調試FMSDR模塊解調FM電臺(2)

    當我們使用FPGA或者STM32模塊配合FMSDR模塊使用的時候,需要從零開始調試這個電路,逐步完成:硬件好壞判斷、8027和MSI001寄
    的頭像 發表于 10-13 15:56 ?3932次閱讀
    使用<b class='flag-5'>STM32</b><b class='flag-5'>調試</b><b class='flag-5'>FMSDR</b><b class='flag-5'>模塊</b>及<b class='flag-5'>解調</b><b class='flag-5'>FM</b><b class='flag-5'>電臺</b>(2)

    使用STM32調試FMSDR模塊解調FM電臺(1)

    當我們使用FPGA或者STM32模塊配合FMSDR模塊使用的時候,需要從零開始調試這個電路,逐步完成:硬件好壞判斷、8027和MSI001寄
    的頭像 發表于 07-06 10:50 ?2056次閱讀
    使用<b class='flag-5'>STM32</b><b class='flag-5'>調試</b><b class='flag-5'>FMSDR</b><b class='flag-5'>模塊</b>及<b class='flag-5'>解調</b><b class='flag-5'>FM</b><b class='flag-5'>電臺</b>(1)

    使用STM32調試FMSDR模塊解調FM電臺(2)

    本文中所有例子中我們都僅給MSI001使用天線,因為QN8027離得很近,發射端不需要使用天線
    的頭像 發表于 07-06 10:54 ?1781次閱讀
    使用<b class='flag-5'>STM32</b><b class='flag-5'>調試</b><b class='flag-5'>FMSDR</b><b class='flag-5'>模塊</b>及<b class='flag-5'>解調</b><b class='flag-5'>FM</b><b class='flag-5'>電臺</b>(2)

    使用STM32調試FMSDR模塊解調FM電臺(4)

    本實驗是在配置MSI001的頻點為101.7Mhz已知交通廣播電臺,觀察喇叭是否能聽到交通廣播聲音,再調整MSI001的輸出幅度。
    的頭像 發表于 07-06 11:05 ?1364次閱讀
    使用<b class='flag-5'>STM32</b><b class='flag-5'>調試</b><b class='flag-5'>FMSDR</b><b class='flag-5'>模塊</b>及<b class='flag-5'>解調</b><b class='flag-5'>FM</b><b class='flag-5'>電臺</b>(4)
    主站蜘蛛池模板: 亚洲国产色婷婷精品综合在线观看 | 国产视频首页 | 免看一级a毛片一片成人不卡 | 国产香蕉精品视频在 | 在线观看www妖精免费福利视频 | 天天干干 | 免费毛片网 | 欧美一级淫片免费播放口 | 日本视频网站在线www色 | 天天性综合 | 伊人98| 波多野结衣在线观看一区二区三区 | 久久伊人成人网 | 亚洲成a人片在线看 | 韩国三级理在线视频观看 | 日韩色区| 日本jlzz | 国产91色综合久久免费分享 | 亚洲视频区| 美女18黄| www操com | 免费的两性视频网站 | 久久综合久久久久 | 性夜影院爽黄a爽免费视频 性瘾高h姚蕊全文免费阅读 | 国产一级特黄特色aa毛片 | 黄色在线观看网址 | 久久久亚洲欧美综合 | 色香视频一sxmv首页 | 韩国男女无遮挡高清性视频 | 大色视频 | 免费看又爽又黄禁片视频1000 | www.av片| 免费一区在线观看 | 人人做人人澡人人人爽 | 美国一级大黄香蕉片 | 国产高清一级视频在线观看 | 美剧免费在线观看 | 91成人免费视频 | 爽a中文字幕一区 | 在线精品91青草国产在线观看 | 欧美性受xxxx|