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

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<8; j++)
		{
			if(0x80 == (ValueToWrite & 0x80))
			{
				ADF4351_OUTPUT_DATA = 1;
			}
			else
			{
				ADF4351_OUTPUT_DATA = 0;
			}
			delay_us(1);
			ADF4351_CLK = 1;
			delay_us(1);
			ValueToWrite <<= 1;
			ADF4351_CLK = 0;	
		}
	}
	ADF4351_OUTPUT_DATA = 0;
	delay_us(1);
	ADF4351_LE = 1;
	delay_us(1);
	ADF4351_LE = 0;
}


void ReadToADF4351(u8 count, u8 *buf)
{
	u8 i = 0;
	u8 j = 0;
	u8 iTemp = 0;
	u8 RotateData = 0;
	
	ADF_Input_GPIOInit();
	ADF4351_CE = 1;
	delay_us(1);
	ADF4351_CLK = 0;
	ADF4351_LE = 0;
	delay_us(1);
	
	for(i = count; i>0; i--)
	{
		for(j = 0; j<8; j++)
		{
			RotateData <<=1;
			delay_us(1);
			iTemp = ADF4351_INPUT_DATA;
			ADF4351_CLK = 1;
			if(0x01 == (iTemp&0x01))
			{
				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 < 35.0)
		Fre = 35.0;
	if(Fre > 4400.0)
		Fre = 4400.0;
	Mod_temp = 1000;
	Fre = ((float)((u32)(Fre*10)))/10;
	
	Fre_temp = Fre;
	for(i = 0; i < 10; i++)
	{
		if(((Fre_temp*N_Mul) >= 2199.9) && ((Fre_temp*N_Mul) <= 4400.1))
			break;
		Mul_Core++;
		N_Mul = N_Mul*2;
	}
	
	multiple = (Fre*N_Mul)/25;		//25:鉴相频率,板载100M参考,经寄存器4分频得25M鉴相。若用户更改为80M参考输入,需将25改为20;10M参考输入,需将25改为2.5,以此类推。。。
	INT_Fre = (u16)multiple;
	Frac_temp = ((u32)(multiple*1000))%1000;
	while(((Frac_temp%5) == 0) && ((Mod_temp%5) == 0))
	{
		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<<15)+(Frac_temp<<3);
	W_ADF4351_R1 = ADF4351_R1_Base + (Mod_temp<<3);
	W_ADF4351_R4 = ADF4351_R4_ON + (Mul_Core<<20);
	
	WriteOneRegToADF4351(ADF4351_RF_OFF);
	WriteOneRegToADF4351(W_ADF4351_R0);
	WriteOneRegToADF4351(W_ADF4351_R1);
	WriteOneRegToADF4351(W_ADF4351_R4);
}

这里面比较常用的是==void ADF4351WriteFreq(float Fre)==这个函数,作用就是写入一个频率Fre,让模块输出该频率的信号,再就是引脚初始化以及ADF4351模块初始化的两个函数,一定要理解,再之后的扫频程序中,也会用到这个库中的函数。

1.扫频程序:顾名思义,就是每隔相同的时间,让输出的频率随一个固定的步进增长,从预设的最小频率一直增长到最大频率,然后再从最小频率开始,不断往复。程序上可以设置一个定时器,每隔一定时间在定时器中断中使当前频率增加一定的值。原理比较好理解,接下来是代码:

#include "stdio.h"
#include "led.h"
#include "ADF4351.h"

u32 SweepMinFre = 1000;  //最小频率
u32 SweepMaxFre = 3500000;	//最大频率
u32 SweepStepFre = 10000;	//步进
u16 SweepTime = 10;	//扫频时间ms
u8 SweepFlag = 1;	//扫频标志位

int main(void)
{
	u16 i=0;

	MY_NVIC_PriorityGroup_Config(NVIC_PriorityGroup_2);	//设置中断分组
	delay_init();	//初始化延时函数
	LED_Init();	//初始化LED接口
	Timerx_Init(99,71);

	//4351初始化
	ADF4351Init();
	ADF4351WriteFreq(400);									//使输出400M频率
	
	while(1)
	{
        ;
	}
}

然后是定时器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

  • 77
    点赞
  • 318
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
ADF4351锁相环模块是一种高性能、低功耗、宽频带的频率合成器芯片。它采用了先进的锁相环技术,可以实现频率合成、频率调制、频率分割和相位锁定等功能。该模块主要应用于通信、雷达、卫星定位、无线电发射等领域,是一种非常重要的电子元器件。 ADF4351锁相环模块采用CMOS工艺生产,工作电压为3.0V~3.6V,工作温度范围为-40℃~85℃。它具有高分辨率、低相噪声、低杂散、高稳定性等特点,可以输出频率范围广泛的信号,最高输出频率可达4.4GHz。 该模块的控制方式多样,可以通过SPI接口进行控制,还可以通过外部参考时钟控制。SPI接口可配置锁相环的各种参数,包括输出频率、频率分割比、相位、功率等等。此外,该模块还具有多种保护功能,如锁定检测、过压保护、过流保护等。 ADF4351锁相环模块的主要优点包括: 1. 高性能:具有高分辨率、低相噪声、低杂散、高稳定性等特点,输出信号质量好。 2. 宽频带:可输出频率范围广泛的信号,最高输出频率可达4.4GHz。 3. 多种控制方式:可通过SPI接口进行控制,也可以通过外部参考时钟控制。 4. 多种保护功能:具有锁定检测、过压保护、过流保护等多种保护功能,提高了使用安全性。 5. 低功耗:模块功耗低,适合需要长时间运行的场合。 ADF4351锁相环模块的主要应用领域包括:通信、雷达、卫星定位、无线电发射等。在这些领域,ADF4351锁相环模块可以实现高精度、高可靠的信号合成和调制,是一种非常重要的电子元器件。
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ziraffe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值