【我的电赛日记(二)】ADF4351锁相环模块 您所在的位置:网站首页 扫频信号图 【我的电赛日记(二)】ADF4351锁相环模块

【我的电赛日记(二)】ADF4351锁相环模块

2023-08-11 04:57| 来源: 网络整理| 查看: 265

ADF4351锁相环模块

这篇日记可能只会对做信号的朋友有帮助,毕竟其他方向使用这个模块的概率不大 锁相环这个模块曾经在2015年全国大学生电子设计竞赛的E题中出现过,大致就是需要使用锁相环模块制作一个可以通过点频的方式输出一个固定的频率信号作为本振源,然后再使用它进行扫频操作。题目大致如下: 在这里插入图片描述

虽然不知道之后还用不用的上,但是学习一下如何使用锁相环模块还是很有必要的🤡

一、硬件连接 首先在硬件方面,我是直接购买的ADF4351的成品模块,价格大概在130左右: 在这里插入图片描述关于这个模块的性能,包括详细的硬件资料我就不多加赘述了,网上很多可以自行去找,首先我们来看一下他的引脚分布以及各个接口的作用: 在这里插入图片描述因为我做的是软件方向,所以我大致介绍几个实际开发中会用到的: 1.数据口:唯一一组需要和单片机主控相连接的IO口,实际应用中也只需要用到GND,CE,LE,DAT以及CLK这5个引脚,连接图大致如下,左边是单片机开发板的接口,右边是ADF4351上的接口。 在这里插入图片描述 2.时钟选择:当把上面两个引脚相连接时,模块默认使用板载晶振,如图所示,此时 W1 端跳线帽接上面两排针。如需输入外部时钟,可将跳线帽切换到下面两排针,将外部时钟信号从 P1 端输入,同时可将板载晶振取下来,避免干扰。没有特殊需求的话默认就可以。

3.信号输出:信号输出 1 和信号输出 2 大小相同,相位相反。

4.外部时钟输入:时钟选择默认时,该接口不用输入任何信号。

接下来是单片机主控,这里我选用的依然是STM32F103ZET6最小系统板,具体连线参照上图即可: 在这里插入图片描述二、STM32软件设计 1.点频程序:也就是输出一个固定频率的信号,这里直接放代码,看不懂可以再问我:

#include "stdio.h" #include "ADF4351.h" #include "delay.h" int main(void) { delay_init(); //延时初始化 delay_ms(300); //ADF4351初始化 ADF4351Init(); ADF4351WriteFreq(400); //设置固定输出频率为400MHZ while(1) { ; } }

上面是点频程序的主函数,接下来是ADF4351.c,里面封装好了使用ADF4351这个模块的主要函数:

#include "ADF4351.h" #include "delay.h" //#define #define ADF4351_R0 ((u32)0X2C8018) #define ADF4351_R1 ((u32)0X8029) #define ADF4351_R2 ((u32)0X10E42) #define ADF4351_R3 ((u32)0X4B3) #define ADF4351_R4 ((u32)0XEC803C) #define ADF4351_R5 ((u32)0X580005) #define ADF4351_R1_Base ((u32)0X8001) #define ADF4351_R4_Base ((u32)0X8C803C) #define ADF4351_R4_ON ((u32)0X8C803C) #define ADF4351_R4_OFF ((u32)0X8C883C) //#define ADF4351_RF_OFF ((u32)0XEC801C) #define ADF4351_PD_ON ((u32)0X10E42) #define ADF4351_PD_OFF ((u32)0X10E02) void ADF_Output_GPIOInit(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStruct); } void ADF_Input_GPIOInit(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_11|GPIO_Pin_12; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING ; GPIO_Init(GPIOC, &GPIO_InitStruct); } void delay (int length) { while (length >0) length--; } void WriteToADF4351(u8 count, u8 *buf) { u8 ValueToWrite = 0; u8 i = 0; u8 j = 0; // ADF_Output_GPIOInit(); ADF4351_CE = 1; delay_us(1); ADF4351_CLK = 0; ADF4351_LE = 0; delay_us(1); for(i = count; i>0; i--) { ValueToWrite = *(buf+i-1); for(j=0; j ADF4351_OUTPUT_DATA = 1; } else { ADF4351_OUTPUT_DATA = 0; } delay_us(1); ADF4351_CLK = 1; delay_us(1); ValueToWrite for(j = 0; j RotateData |= 1; } delay_us(1); ADF4351_CLK = 0; } *(buf+i-1) = RotateData; } delay_us(1); ADF4351_LE = 1; delay_us(1); ADF4351_LE = 0; } void ADF4351Init(void) { u8 buf[4] = {0,0,0,0}; ADF_Output_GPIOInit(); buf[3] = 0x00; buf[2] = 0x58; buf[1] = 0x00; //write communication register 0x00580005 to control the progress buf[0] = 0x05; //to write Register 5 to set digital lock detector WriteToADF4351(4,buf); buf[3] = 0x00; buf[2] = 0xec; //(DB23=1)The signal is taken from the VCO directly;(DB22-20:4H)the RF divider is 16;(DB19-12:50H)R is 80 buf[1] = 0x80; //(DB11=0)VCO powerd up; buf[0] = 0x3C; //(DB5=1)RF output is enabled;(DB4-3=3H)Output power level is 5 WriteToADF4351(4,buf); buf[3] = 0x00; buf[2] = 0x00; buf[1] = 0x04; //(DB14-3:96H)clock divider value is 150. buf[0] = 0xB3; WriteToADF4351(4,buf); buf[3] = 0x00; buf[2] = 0x01; //(DB6=1)set PD polarity is positive;(DB7=1)LDP is 6nS; buf[1] = 0x0E; //(DB8=0)enable fractional-N digital lock detect; buf[0] = 0x42; //(DB12-9:7H)set Icp 2.50 mA; WriteToADF4351(4,buf); //(DB23-14:1H)R counter is 1 buf[3] = 0x00; buf[2] = 0x00; buf[1] = 0x80; //(DB14-3:6H)MOD counter is 6; buf[0] = 0x29; //(DB26-15:6H)PHASE word is 1,neither the phase resync WriteToADF4351(4,buf); //nor the spurious optimization functions are being used //(DB27=1)prescaler value is 8/9 buf[3] = 0x00; buf[2] = 0x2c; buf[1] = 0x80; buf[0] = 0x18; //(DB14-3:0H)FRAC value is 0; WriteToADF4351(4,buf); //(DB30-15:140H)INT value is 320; } void WriteOneRegToADF4351(u32 Regster) { u8 buf[4] = {0,0,0,0}; buf[3] = (u8)((Regster>>24)&(0X000000FF)); buf[2] = (u8)((Regster>>16)&(0X000000FF)); buf[1] = (u8)((Regster>>8) &(0X000000FF)); buf[0] = (u8)((Regster)&(0X000000FF)); WriteToADF4351(4,buf); } void ADF4351_Init_some(void) { WriteOneRegToADF4351(ADF4351_R2); WriteOneRegToADF4351(ADF4351_R3); WriteOneRegToADF4351(ADF4351_R5); } void ADF4351WriteFreq(float Fre) // fre单位MHz -> (xx.x) M Hz { u16 Fre_temp, N_Mul = 1, Mul_Core = 0; u16 INT_Fre, Frac_temp, Mod_temp, i; u32 W_ADF4351_R0 = 0, W_ADF4351_R1 = 0, W_ADF4351_R4 = 0; float multiple; if(Fre 4400.0) Fre = 4400.0; Mod_temp = 1000; Fre = ((float)((u32)(Fre*10)))/10; Fre_temp = Fre; for(i = 0; i Frac_temp = Frac_temp/5; Mod_temp = Mod_temp/5; } while(((Frac_temp%2) == 0)&&((Mod_temp%2) == 0)) { Frac_temp = Frac_temp/2; Mod_temp = Mod_temp/2; } Mul_Core %= 7; W_ADF4351_R0 = (INT_Fre ; } }

然后是定时器time.c中的设置:

#include "timer.h" #include "led.h" #include "ADF4351.h" //通用定时器中断初始化 //这里时钟选择为APB1的2倍,而APB1为36M //arr:自动重装值。 //psc:时钟预分频数 //这里使用的是定时器3! void Timerx_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能 //定时器初始化 //arr,psc TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms TIM_TimeBaseStructure.TIM_Prescaler = psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim 分频因子 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 TIM_ITConfig( //使能或者失能指定的TIM中断 允许更新中断 TIM4, //TIM2 TIM_IT_Update | //TIM 更新(溢出)中断 TIM_IT_Trigger, //TIM 触发中断 (捕获中断等) 地址值 ((uint16_t)0x0040) ENABLE //使能 ); NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 TIM_Cmd(TIM4, ENABLE); //使能TIMx(定时器)外设 开启定时器 } u32 count=0, count1=0, NowFre=0; extern u32 SweepMinFre; extern u32 SweepMaxFre; extern u32 SweepStepFre; extern u16 SweepTime;//ms extern u8 SweepFlag; void TIM4_IRQHandler(void) //TIM3中断 { if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 { TIM_ClearITPendingBit(TIM4, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源 if(SweepFlag) { count++; if(count>=10*SweepTime) { count=0; count1++; NowFre = SweepMinFre+SweepStepFre*count1; if(NowFre>SweepMaxFre) count1 = 0; //写数据 ADF4351WriteFreq((double)NowFre/1000); } } } }

点频和扫频的代码就介绍到这,最后是写给需要使用外部时钟输入的人看的,如果想要使用外部输入信号作为时钟的话,只需要修改ADF4351.c中的这一部分: 使用外部时钟时修改时钟频率multiple = (Fre*N_Mul)/25; multiple是鉴相频率,以板载100MHz作为参考,经过寄存器4分频得25M鉴相。若需要更改为80M 参考输入,需将25改为 20;10M 参考输入,需将25改为2.5,以此类推。 同时一定要注意:选择外部时钟时,频率最好在 100MHZ 以内!!!

200MHZ的点频效果大概如下: 在这里插入图片描述 输出算是比较标准的正弦波,但输出为35MHZ这种时,可以看见波形已经变形,这是由于芯片为数字分频,产生的信号谐波分量比较大,波形不是标准正弦波,可外加滤波器来改善波形: 在这里插入图片描述

最后我把程序的源代码放在下方,大家有需要可以借鉴学习! https://download.csdn.net/download/qq_45416203/12722477



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有