实现stm32在FSK调制解调器的综合设计
总的设计思路如下:
本文引用地址:https://www.eepw.com.cn/article/201611/316111.htm首先是基带信号的产生,它也是我们要调制和解调的目标。基带信号由一连串随机的码元序列构成,为了模拟随机的码元序列,笔者用定时器设计8位的PN码序列,码元速率为2000B/s。定时器3定时0.5ms,每进入一次中断,变量num加一,设置一次IO引脚电平,8位PN码只需设置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);}}
接下来要产生载波,载波就是正弦波无疑。这里笔者的载波频率要求是4khz和8khz。正弦波的产生用的是stm32的DMA+DAC+TIM2。正弦波的数据用正弦波数据发生器产生,采样点数64,精度12位,保存在Sine12bit[]数组,但是传送给DMA的正弦波数据不是这些原始的数据,而是将这些数据进行了进一步的处理:
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){... //省去无关代码for (Idx = 0; Idx < 64; Idx++){Sine12bit[Idx] = Sine12bit[Idx]*8/10+500; //防止出现底部失真}... //省去无关代码}
#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; //设为向下计数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信号,这个组合当然不是随意组合,是要在基带信号的控制下进行。代码在主函数执行,如下:
int main(void){... //初始化代码while(1){if(Base_Signal == 1){TIM2->ARR = 140;;}if(Base_Signal == 0){TIM2->ARR = 280;}}}
经过上述一番折腾,调制总算是搞定了。
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);//设置下一次触发为下降沿触发TIM_SetCounter(TIM1,0);//清空TIM1->CCR1寄存器的值TIM1CH1_CAPTURE_VAL = 0;//变量TIM1CH1_CAPTURE_VAL用于存储TIM1->CCR1寄存器的值flag_falling = 1;//置位标志位,标志下一次进入中断后检测到下降沿}else //检测到下降沿{TIM_OC1PolarityConfig(TIM1,TIM_ICPolarity_Rising);//设置下一次触发为上升沿触发TIM1CH1_CAPTURE_VAL=TIM_GetCapture1(TIM1);//读取TIM1->CCR1寄存器的值flag_falling = 0;//清除标志位,标志下一次进入中断后检测到上升沿if(TIM1CH1_CAPTURE_VAL >= 100)//设定阈值,与TIM1CH1_CAPTURE_VAL进行比较{First_jietiao = 0;}else{First_jietiao = 1;}}TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);}
TIM1_Cap_Init(0XFFFF,71); //以1MHZ的频率计数
看到了吧,0xFFFF,多大的数~其实也不大,只不过对于我们要捕获的FSK信号来说它避免了更新中断对捕获造成的影响,也就是说当我们捕获到下降沿时得到的TIM1->CCR1寄存器的值就是我们想得到的时间,与计数值溢出多少次并无关系。注意:当捕获的波形频率较高时可以这么做,但是如果波形频率较低时最好使能更新中断,在更新中断里保存中断次数,得到的结果更准确。
然而这只是我们初步解调出来的结果,由于4khz与8khz之间的过渡带影响,最终得到的码元序列“1”的持续时间长于码元为“0”的持续时间,信号的码速率不是2000B/s,所以我们需要进行二次解调。
二次解调的关键在于定时器TIM5的同步作用。笔者用TIM5定时2khz,在初步解调信号的边沿处先延时150us,然后开始同步,通过判断初步解调信号的码元序列,得到二次解调信号的码元。
在TIM1中断函数里面:
u8 a=1; //a为全局变量
if(flag_falling == 0 && a == 1)//捕获到下降沿时开始同步(下降沿亦即初步解调信号的边沿){delay_us(150);TIM_Cmd(TIM5, ENABLE); //只需要执行一次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 );}}
评论