實現(xiàn)stm32在FSK調(diào)制解調(diào)器的綜合設(shè)計
總的設(shè)計思路如下:
本文引用地址:http://butianyuan.cn/article/201611/316111.htm首先是基帶信號的產(chǎn)生,它也是我們要調(diào)制和解調(diào)的目標(biāo)?;鶐盘栍梢贿B串隨機的碼元序列構(gòu)成,為了模擬隨機的碼元序列,筆者用定時器設(shè)計8位的PN碼序列,碼元速率為2000B/s。定時器3定時0.5ms,每進(jìn)入一次中斷,變量num加一,設(shè)置一次IO引腳電平,8位PN碼只需設(shè)置8次,然后num清零。
TIM3_Init(499,71); //基帶信號u8 num=0;void TIM3_IRQHandler(void){if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){num++;switch (num){case 1: Base_Signal = 1; break;case 2: Base_Signal = 0; break;case 3: Base_Signal = 0; break;case 4: Base_Signal = 0; break;case 5: Base_Signal = 1; break;case 6: Base_Signal = 0; break;case 7: Base_Signal = 1; break;case 8: Base_Signal = 0; break; //pn碼序列}if(num == 8)num = 0;TIM_ClearITPendingBit(TIM3, TIM_IT_Update);}}
接下來要產(chǎn)生載波,載波就是正弦波無疑。這里筆者的載波頻率要求是4khz和8khz。正弦波的產(chǎn)生用的是stm32的DMA+DAC+TIM2。正弦波的數(shù)據(jù)用正弦波數(shù)據(jù)發(fā)生器產(chǎn)生,采樣點數(shù)64,精度12位,保存在Sine12bit[]數(shù)組,但是傳送給DMA的正弦波數(shù)據(jù)不是這些原始的數(shù)據(jù),而是將這些數(shù)據(jù)進(jìn)行了進(jìn)一步的處理:
uint16_t Sine12bit[64] = {0x7FF,0x8C8,0x98E,0xA51,0xB0F,0xBC4,0xC71,0xD12,0xDA7,0xE2E,0xEA5,0xF0D,0xF63,0xFA6,0xFD7,0xFF5,0xFFE,0xFF5,0xFD7,0xFA6,0xF63,0xF0D,0xEA5,0xE2E,0xDA7,0xD12,0xC71,0xBC4,0xB0F,0xA51,0x98E,0x8C8,0x7FF,0x736,0x670,0x5AD,0x4EF,0x43A,0x38D,0x2EC,0x257,0x1D0,0x159,0x0F1,0x09B,0x058,0x027,0x009,0x000,0x009,0x027,0x058,0x09B,0x0F1,0x159,0x1D0,0x257,0x2EC,0x38D,0x43A,0x4EF,0x5AD,0x670,0x736};uint32_t Idx = 0;int main(void){... //省去無關(guān)代碼for (Idx = 0; Idx < 64; Idx++){Sine12bit[Idx] = Sine12bit[Idx]*8/10+500; //防止出現(xiàn)底部失真}... //省去無關(guān)代碼}
#define DAC_DHR12R2_Address 0x40007414void DMAx_Init(void){DMA_InitTypeDef DMA_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;/* DMA1 clock enable */RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);/* GPIOA Periph clock enable */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);/* DAC Periph clock enable */RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);/* Once the DAC channel is enabled, the corresponding GPIO pin is automaticallyconnected to the DAC converter. In order to avoid parasitic consumption,the GPIO pin should be configured in analog */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置為模擬輸入,抗噪聲干擾GPIO_Init(GPIOA, &GPIO_InitStructure);/* DMA1 channel4 configuration */DMA_DeInit(DMA2_Channel4);DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12R2_Address;//DAC通道2的12位右對齊寄存器地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Sine12bit;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;DMA_InitStructure.DMA_BufferSize = 64;//采樣64點,故緩存大小為64DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA2_Channel4,&DMA_InitStructure);DMA_Cmd(DMA2_Channel4, ENABLE);}
void TIM2_DAC_Init(u16 arr,u16 psc){TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;DAC_InitTypeDef DAC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);TIM_TimeBaseStructure.TIM_Period = arr;TIM_TimeBaseStructure.TIM_Prescaler = psc;TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down; //設(shè)為向下計數(shù)TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO;DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //使能輸出緩存DAC_Init(DAC_Channel_2, &DAC_InitStructure);DAC_Cmd(DAC_Channel_2, ENABLE);DAC_DMACmd(DAC_Channel_2, ENABLE);TIM_Cmd(TIM2, ENABLE);}
生成正弦波后自然是要把兩個正弦波組合在一起形成FSK信號,這個組合當(dāng)然不是隨意組合,是要在基帶信號的控制下進(jìn)行。代碼在主函數(shù)執(zhí)行,如下:
int main(void){... //初始化代碼while(1){if(Base_Signal == 1){TIM2->ARR = 140;;}if(Base_Signal == 0){TIM2->ARR = 280;}}}
經(jīng)過上述一番折騰,調(diào)制總算是搞定了。
void TIM1_Cap_Init(u16 arr,u16 psc){GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM1_ICInitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_ResetBits(GPIOA,GPIO_Pin_8);TIM_TimeBaseStructure.TIM_Period = arr;TIM_TimeBaseStructure.TIM_Prescaler =psc;TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);TIM1_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01TIM1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;TIM1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;TIM1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;TIM1_ICInitStructure.TIM_ICFilter = 0x00;TIM_ICInit(TIM1, &TIM1_ICInitStructure);NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);TIM_ITConfig(TIM1,TIM_IT_CC1,ENABLE);TIM_Cmd(TIM1,ENABLE );}
u8 flag_falling;int TIM1CH1_CAPTURE_VAL;void TIM1_CC_IRQHandler(void){if(flag_falling == 0) //檢測到上升沿{TIM_OC1PolarityConfig(TIM1,TIM_ICPolarity_Falling);//設(shè)置下一次觸發(fā)為下降沿觸發(fā)TIM_SetCounter(TIM1,0);//清空TIM1->CCR1寄存器的值TIM1CH1_CAPTURE_VAL = 0;//變量TIM1CH1_CAPTURE_VAL用于存儲TIM1->CCR1寄存器的值flag_falling = 1;//置位標(biāo)志位,標(biāo)志下一次進(jìn)入中斷后檢測到下降沿}else //檢測到下降沿{TIM_OC1PolarityConfig(TIM1,TIM_ICPolarity_Rising);//設(shè)置下一次觸發(fā)為上升沿觸發(fā)TIM1CH1_CAPTURE_VAL=TIM_GetCapture1(TIM1);//讀取TIM1->CCR1寄存器的值flag_falling = 0;//清除標(biāo)志位,標(biāo)志下一次進(jìn)入中斷后檢測到上升沿if(TIM1CH1_CAPTURE_VAL >= 100)//設(shè)定閾值,與TIM1CH1_CAPTURE_VAL進(jìn)行比較{First_jietiao = 0;}else{First_jietiao = 1;}}TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);}
TIM1_Cap_Init(0XFFFF,71); //以1MHZ的頻率計數(shù)
看到了吧,0xFFFF,多大的數(shù)~其實也不大,只不過對于我們要捕獲的FSK信號來說它避免了更新中斷對捕獲造成的影響,也就是說當(dāng)我們捕獲到下降沿時得到的TIM1->CCR1寄存器的值就是我們想得到的時間,與計數(shù)值溢出多少次并無關(guān)系。注意:當(dāng)捕獲的波形頻率較高時可以這么做,但是如果波形頻率較低時最好使能更新中斷,在更新中斷里保存中斷次數(shù),得到的結(jié)果更準(zhǔn)確。
然而這只是我們初步解調(diào)出來的結(jié)果,由于4khz與8khz之間的過渡帶影響,最終得到的碼元序列“1”的持續(xù)時間長于碼元為“0”的持續(xù)時間,信號的碼速率不是2000B/s,所以我們需要進(jìn)行二次解調(diào)。
二次解調(diào)的關(guān)鍵在于定時器TIM5的同步作用。筆者用TIM5定時2khz,在初步解調(diào)信號的邊沿處先延時150us,然后開始同步,通過判斷初步解調(diào)信號的碼元序列,得到二次解調(diào)信號的碼元。
在TIM1中斷函數(shù)里面:
u8 a=1; //a為全局變量
if(flag_falling == 0 && a == 1)//捕獲到下降沿時開始同步(下降沿亦即初步解調(diào)信號的邊沿){delay_us(150);TIM_Cmd(TIM5, ENABLE); //只需要執(zhí)行一次a = 0;}
void TIM5_IRQHandler(void){if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET){if(First_jietiao == 1)out_put = 1;elseout_put = 0;TIM_ClearITPendingBit(TIM5, TIM_IT_Update );}}
評論