SI5351使用方法

项目需求:原本使用的是AD9833,但是一片47元,较贵!。但是SI5351一片只有5.9元,考虑成本使用。低成本,且以5HZ步进扫频。

参考资料:si5351时钟芯片详解-优快云博客https://blog.youkuaiyun.com/define_Motion/article/details/128301947

stm32 si5351使用(软件模拟i2c和硬件i2c)-优快云博客https://blog.youkuaiyun.com/weixin_43387612/article/details/106032137

https://blog.youkuaiyun.com/qq_43600271/article/details/100554832https://blog.youkuaiyun.com/qq_43600271/article/details/100554832

程序代码:

IIC.c

#include "myiic.h"
#include "delay.h"



// 初始化IIC的IO口
void I2C2_Soft_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;   // 定义GPIO结构体
	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  // 打开GPIOB口时钟
	
//    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  // 输出
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏
    GPIO_InitStructure.GPIO_Pin = Pin_SCL | Pin_SDA ; // IIC对应IO口
//    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 50MHZ
    GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化GPIO
	
    I2C2_Stop();
}
 
// 发送IIC起始信号
bool I2C2_Start(void)
{
    Pin_SCL_H; // 拉高时钟线
    Pin_SDA_H; // 拉高信号线
    I2C2_Delay1us();
    if(!Read_SDA_Pin)		return false;
    Pin_SDA_L;
    I2C2_Delay1us();
    Pin_SDA_L;
    I2C2_Delay1us();
    return true;
}
 
// 发送IIC停止信号
bool I2C2_Stop(void)
{
    Pin_SCL_H;
    Pin_SDA_L;
    I2C2_Delay1us();
    if(Read_SDA_Pin)	return false;
    Pin_SDA_H;
    I2C2_Delay1us();
    if(!Read_SDA_Pin) return false;
    Pin_SDA_H;
    I2C2_Delay1us();	
    return true;
}
 
// IIC发送ACK信号
void I2C2_Ack(void)
{
    Pin_SCL_L;
    I2C2_Delay1us();
    Pin_SDA_L;	
    Pin_SCL_H;
    I2C2_Delay1us();
    Pin_SCL_L;
    Pin_SDA_H;
    I2C2_Delay1us();
}
 
// IIC不发送ACK信号
void I2C2_NAck(void)
{
    Pin_SCL_L;
    I2C2_Delay1us();	
    Pin_SDA_H;
    Pin_SCL_H;
    I2C2_Delay1us();
    Pin_SCL_L;
    I2C2_Delay1us();
}
 
// IIC等待ACK信号
uint8_t I2C2_Wait_Ack(void)
{
    Pin_SCL_L;
    I2C2_Delay1us();	
    Pin_SDA_H;
    Pin_SCL_H;
    I2C2_Delay1us();	
    if(Read_SDA_Pin)
    {
	Pin_SCL_L;
	I2C2_Delay1us();
	return false;
    }
    Pin_SCL_L;
    I2C2_Delay1us();
    return true;
}
 
// IIC发送一个字节
void I2C2_Send_Byte(uint8_t txd)
{
    for(uint8_t i=0; i<8; i++)
    {
	Pin_SCL_L;
	I2C2_Delay1us();
	if(txd & 0x80)
	    Pin_SDA_H;
	else
	    Pin_SDA_L;
	    txd <<= 1;
	    Pin_SCL_H;
	    I2C2_Delay1us();
    }
}
 
// IIC读取一个字节
uint8_t	I2C2_Read_Byte(void)
{
    uint8_t rxd = 0;
    for(uint8_t i=0; i<8; i++)
    {
	rxd <<= 1;
	Pin_SCL_L;
	I2C2_Delay1us();
	Pin_SCL_H;	
	I2C2_Delay1us();		
	if(Read_SDA_Pin)
	{
	    rxd |= 0x01;
	}
    }
    return rxd;
}
 
// 向从机指定地址写数据
bool I2C_Write_REG(uint8_t SlaveAddress, uint8_t REG_Address,uint8_t REG_data)
{
    if(!I2C2_Start())		return false;
    I2C2_Send_Byte(SlaveAddress);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    I2C2_Send_Byte(REG_Address);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    I2C2_Send_Byte(REG_data);
    if(!I2C2_Wait_Ack()) { I2C2_Stop(); return false;	}
    if(!I2C2_Stop()) return false;
    return true;
}
 
// 从设备中读取数据
uint8_t I2C2_Read_REG(uint8_t SlaveAddress,uint8_t REG_Address)
{
    uint8_t data;
    if(!I2C2_Start())	return false;
    I2C2_Send_Byte(SlaveAddress);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    I2C2_Send_Byte(REG_Address);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    if(!I2C2_Start())	return false;
    I2C2_Send_Byte(SlaveAddress + 1);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    data = I2C2_Read_Byte();
    I2C2_NAck();
    if(!I2C2_Stop())	return false;	
    return data;
}
 
// 连续写N个字节
bool I2C2_Write_NByte(uint8_t SlaveAddress, uint8_t REG_Address, uint8_t* buf, uint8_t len)
{
    if(!I2C2_Start())return false;
    I2C2_Send_Byte(SlaveAddress);  //发送设备地址+写信号
    if(!I2C2_Wait_Ack()){I2C2_Stop(); return false;}
    I2C2_Send_Byte(REG_Address);   
    if(!I2C2_Wait_Ack()){I2C2_Stop(); return false;}
    for(uint16_t i=0; i<len; i++)
    {
        I2C2_Send_Byte(buf[i]);
	if(i<len-1)
	{
            if(!I2C2_Wait_Ack()){I2C2_Stop(); return false;}
	}
    }
    I2C2_Stop();
    return true;
}

bool I2C2_CheckDevice(uint8_t SlaveAddress)
{
    if(!I2C2_Start())	return false;
    I2C2_Send_Byte(SlaveAddress);
    if(!I2C2_Wait_Ack())
    {
	I2C2_Stop();
	return false;		
    }
    if(!I2C2_Stop())	return false;	
    return true;	
}


bool my_I2C_sendREG(uint8_t REG_Address,uint8_t REG_data)
{
    if(!I2C2_Start())		return false;
    I2C2_Send_Byte(0xC0);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    I2C2_Send_Byte(REG_Address);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    I2C2_Send_Byte(REG_data);
    if(!I2C2_Wait_Ack()) { I2C2_Stop(); return false;	}
    if(!I2C2_Stop()) return false;
    return true;
}

uint8_t my_I2C2_Read_REG(uint8_t REG_Address)
{
    uint8_t data;
    if(!I2C2_Start())	return false;
    I2C2_Send_Byte(0xC0);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    I2C2_Send_Byte(REG_Address);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    if(!I2C2_Start())	return false;
    I2C2_Send_Byte(0xC1);
    if(!I2C2_Wait_Ack()) { I2C2_Stop();	return false;	}
    data = I2C2_Read_Byte();
    I2C2_NAck();
    if(!I2C2_Stop())	return false;	
    return data;
}

void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom)
{
  uint32_t P1;					// PLL config register P1
  uint32_t P2;					// PLL config register P2
  uint32_t P3;					// PLL config register P3

  P1 = (uint32_t)(128 * ((float)num / (float)denom));
  P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
  P2 = (uint32_t)(128 * ((float)num / (float)denom));
  P2 = (uint32_t)(128 * num - denom * P2);
  P3 = denom;

  my_I2C_sendREG(pll + 0, (P3 & 0x0000FF00) >> 8);
  my_I2C_sendREG(pll + 1, (P3 & 0x000000FF));
  my_I2C_sendREG(pll + 2, (P1 & 0x00030000) >> 16);
  my_I2C_sendREG(pll + 3, (P1 & 0x0000FF00) >> 8);
  my_I2C_sendREG(pll + 4, (P1 & 0x000000FF));
  my_I2C_sendREG(pll + 5, ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
  my_I2C_sendREG(pll + 6, (P2 & 0x0000FF00) >> 8);
  my_I2C_sendREG(pll + 7, (P2 & 0x000000FF));
}


void setupMultisynth(uint8_t synth,uint32_t divider,uint8_t rDiv)
{
  uint32_t P1;					// Synth config register P1
  uint32_t P2;					// Synth config register P2
  uint32_t P3;					// Synth config register P3

  P1 = 128 * divider - 512;
  P2 = 0;							// P2 = 0, P3 = 1 forces an integer value for the divider
  P3 = 1;

  my_I2C_sendREG(synth + 0,   (P3 & 0x0000FF00) >> 8);
  my_I2C_sendREG(synth + 1,   (P3 & 0x000000FF));
  my_I2C_sendREG(synth + 2,   ((P1 & 0x00030000) >> 16) | rDiv);
  my_I2C_sendREG(synth + 3,   (P1 & 0x0000FF00) >> 8);
  my_I2C_sendREG(synth + 4,   (P1 & 0x000000FF));
  my_I2C_sendREG(synth + 5,   ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16));
  my_I2C_sendREG(synth + 6,   (P2 & 0x0000FF00) >> 8);
  my_I2C_sendREG(synth + 7,   (P2 & 0x000000FF));
}


void si5351aSetFrequency(uint32_t frequency , uint8_t Chanal )
{
  uint32_t pllFreq;
  uint32_t xtalFreq = XTAL_FREQ;// Crystal frequency
  uint32_t l;
  float f;
  uint8_t mult;
  uint32_t num;
  uint32_t denom;
  uint32_t divider;
	
	frequency=32*frequency;        //输出KHz需要配置RO_DIV[2:0]位
  divider = 600000000 / frequency;// Calculate the division ratio. 900,000,000 is the maximum internal 
                                                                  // PLL frequency: 900MHz
  if (divider % 2) divider--;		// Ensure an even integer division ratio

  pllFreq = divider * frequency;	// Calculate the pllFrequency: the divider * desired output frequency

  mult = pllFreq / xtalFreq;		// Determine the multiplier to get to the required pllFrequency
  l = pllFreq % xtalFreq;			// It has three parts:
  f = l;							// mult is an integer that must be in the range 15..90
  f *= 1048575;					// num and denom are the fractional parts, the numerator and denominator
  f /= xtalFreq;					// each is 20 bits (range 0..1048575)
  num = f;						// the actual multiplier is  mult + num / denom
  denom = 1048575;				// For simplicity we set the denominator to the maximum 1048575
  // Set up PLL A with the calculated multiplication ratio
  setupPLL(SI_SYNTH_PLL_A, mult, num, denom);
                                                                  // Set up MultiSynth divider 0, with the calculated divider. 
                                                                  // The final R division stage can divide by a power of two, from 1..128. 
                                                                  // reprented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
                                                                  // If you want to output frequencies below 1MHz, you have to use the 
                                                                  // final R division stage
  if( Chanal == 0 ){
		
		
		
		setupMultisynth(SI_SYNTH_MS_0,divider,0X50);        //配置RO_DIV[2:0]位
                                                                  // Reset the PLL. This causes a glitch in the output. For small changes to 
                                                                  // the parameters, you don't need to reset the PLL, and there is no glitch
		my_I2C_sendREG(SI_PLL_RESET,0xA0);	
                                                                  // Finally switch on the CLK0 output (0x4F)
                                                                  // and set the MultiSynth0 input to be PLL A
		my_I2C_sendREG(SI_CLK0_CONTROL, 0x4F|SI_CLK_SRC_PLL_A);
	}
	else if ( Chanal == 1 ){
		setupMultisynth(SI_SYNTH_MS_1,divider,SI_R_DIV_1);
		my_I2C_sendREG(SI_PLL_RESET,0xA0);	
		my_I2C_sendREG(SI_CLK1_CONTROL, 0x4F|SI_CLK_SRC_PLL_A);
	}
		else if ( Chanal == 2 ){
		setupMultisynth(SI_SYNTH_MS_2,divider,SI_R_DIV_1);
		my_I2C_sendREG(SI_PLL_RESET,0xA0);	
		my_I2C_sendREG(SI_CLK2_CONTROL, 0x4F|SI_CLK_SRC_PLL_A);
		}
}


























IIC.h

#ifndef __I2C_H
#define	__I2C_H



#include "stm32f10x.h"

typedef unsigned  short int   uint;
typedef enum {false = 0, true = !false} bool;

#define	I2C2_GPIOx  GPIOB
#define Pin_SCL		GPIO_Pin_8//8
#define Pin_SDA		GPIO_Pin_9//9
 
#define Pin_SCL_L		I2C2_GPIOx->ODR &= ~Pin_SCL
#define Pin_SCL_H		I2C2_GPIOx->ODR |= Pin_SCL
 
#define Pin_SDA_L		I2C2_GPIOx->ODR &= ~Pin_SDA
#define Pin_SDA_H		I2C2_GPIOx->ODR |= Pin_SDA
 
#define Read_SDA_Pin	I2C2_GPIOx->IDR & Pin_SDA


#define SI_CLK0_CONTROL	16			// Register definitions
#define SI_CLK1_CONTROL	17
#define SI_CLK2_CONTROL	18
#define SI_SYNTH_PLL_A	26
#define SI_SYNTH_PLL_B	34
#define SI_SYNTH_MS_0		42
#define SI_SYNTH_MS_1		50
#define SI_SYNTH_MS_2		58
#define SI_PLL_RESET		177

#define SI_R_DIV_1		0x00						// R-division ratio definitions
#define SI_R_DIV_2		0b00010000
#define SI_R_DIV_4		0b00100000
#define SI_R_DIV_8		0b00110000
#define SI_R_DIV_16		0b01000000
#define SI_R_DIV_32		0b01010000
#define SI_R_DIV_64		0b01100000
#define SI_R_DIV_128		0b01110000

#define SI_CLK_SRC_PLL_A	0x00
#define SI_CLK_SRC_PLL_B	0b00100000
#define XTAL_FREQ	25000000			// Crystal frequency




static inline void I2C2_Delay1us(void)
{
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
	__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();	
}	
 
 

 
void I2C2_Soft_Init(void);			
bool I2C2_Start(void);						
bool I2C2_Stop(void);	  				
void I2C2_Send_Byte(uint8_t txd);
uint8_t I2C2_Read_Byte(void);
uint8_t I2C2_Wait_Ack(void); 		
void I2C2_Ack(void);							
void I2C2_NAck(void);						
 
bool I2C2_Write_REG(uint8_t SlaveAddress,uint8_t REG_Address,uint8_t REG_data);
uint8_t I2C2_Read_REG(uint8_t SlaveAddress,uint8_t REG_Address);
bool I2C2_Write_NByte(uint8_t SlaveAddress, uint8_t REG_Address, uint8_t* buf, uint8_t len);
bool I2C2_Read_NByte(uint8_t SlaveAddress, uint8_t REG_Address, uint8_t* buf, uint8_t len);
//建议使用这个函数来测试是否有通信
bool I2C2_CheckDevice(uint8_t SlaveAddress);

//这里的代码和硬件i2c重复了,下面会给出
void si5351aSetFrequency(uint32_t frequency , uint8_t Chanal );
void setupPLL(uint8_t pll, uint8_t mult, uint32_t num, uint32_t denom);
void setupMultisynth(uint8_t synth,uint32_t divider,uint8_t rDiv);

#endif

















main.c

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "pwm.h"
#include "myiic.h"


 int main(void)
 {	
	uint8_t testAddr = 0;
//	int Fre_1M = 15000000;
	 	int Fre_1M = 55020;
//	int fre = 55000;
	 
	 
	 
	 delay_init();
	SystemInit();
	I2C2_Soft_Init();
	 LED_Init();
	
//	si5351aSetFrequency(1*Fre_1M , 0);//CLK0
	 
	 
	 
//	 	si5351aSetFrequency(55000 , 0);//CLK0
//	si5351aSetFrequency(2*Fre_1M , 1);
//	si5351aSetFrequency(3*Fre_1M , 2);
	
	while(1)
	{		
		si5351aSetFrequency(1*Fre_1M , 0);//CLK0
		Fre_1M=Fre_1M+5;
		
		
		delay_ms(500);
		LED0=0;		
		delay_ms(500);
			LED0=1;
		
	}

}	 
 






























适用于家庭无线电设备,例如超外差接收机,SDR,HAM QRP收发器或RF发生器。 硬件部件: 面包板(通用) × 1个 Arduino Nano R3 × 1个 带按钮的旋转编码器 × 1个 Adafruit SSD1306 128X64 OLED显示屏× 1个 Adafruit SI5351时钟发电模块× 1个 拨动开关,SPDT × 2个 松下RCA插孔,用于RF输出设置× 2个 电容100 nF × 3 电容器10 µF × 1个 电容10 nF × 2个 电阻1k欧姆 × 1个 电感100 uH × 1个 软件应用程序和在线服务 Arduino IDE 这是VFO(变频振荡器)的项目,可用于自制设备,例如超外差接收器,DCR,SDR或Ham QRP收发器。它还具有用于信号强度(S-Meter)和20 Band预设的条形图指示器。也可以用作RF /时钟发生器。这是新版本(V.2),我更新了以前的项目,它包含新功能。 特征: 工作范围为10kHz至225MHz。 1Hz,10Hz,1kHz,5kHz,10kHz和1MHz的调谐步长。 中频(IF)偏移(+或-)可调。 BCB和HAM频率的20个频段预设(快捷方式)。 发电机功能模式。 RX / TX模式选择器,用于Homebrew QRP收发器。 通过模拟输入(ADC)的信号表的条形图。 用作Homebrew无线电接收器(如超外差,SDR,直接转换和Homebrew QRP收发器)上的本地振荡器。 用作简单的RF /时钟发生器,用于校准参考或时钟生成。 可与Arduino Uno,Nano和Pro Mini一起使用使用通用的128x64 I2C OLED SSD1306显示器和Si5351模块。 I2C数据传输,仅需2条线即可连接显示器/ Si5351和arduino。 频率生成的高稳定性和精度。 简单但非常有效且免费。 设置: 原理图/接线: 演示视频: 指示: 在Arduino IDE上打开scketch,安装所有必需的库。 选择首选项(请参阅注释)并编译草图,然后将其加载到Arduino Nano,Uno或Pro Mini。 按照示意图连接Arduino,Display,Si5351模块,旋转编码器等。 打开Arduino的电源。 旋转旋转编码器以调高或调低频率。 按下按钮1更改频率步进调谐。可用的步进为1Hz,10Hz,1kHz,5kHz,10kHz和1MHz。 按下按钮2浏览(选择)20个频段预设或进入发生器模式。 打开/关闭开关SW 2,以从RX模式更改为TX模式。RX模式= SW 2开路,TX模式= SW 2接地。在TX模式下,不会从RF输出中添加/减去IF值。这是在Homebrew QRP收发器中使用的理想选择。 将无线电的S-Meter输出信号连接到X2连接器(S-Meter输入)。该输入具有可调的灵敏度,必须在Sketch中调整增益,以接受500mV至5V(最大)的信号。 关于用户首选项的注意事项: -可以更改原理图上的以下项目: #define IF 455 //输入您的IF频率,例如:455 = 455kHz,10700 = 10.7MHz,0 =直接转换接收器或RF发生器,+将加和-将减去ffset。 #define BAND_INIT 7 //在启动时输入初始频段(1-21),例如:1 =频率发生器,2 = 800kHz(MW),7 = 7.2MHz(40m),11 = 14.1MHz(20m)。 #define XT_CAL_F 33000 // Si5351校准系数,调整为可得10MHz。增加该值将降低频率,反之亦然。 #define S_GAIN 303 //调整信号仪表A / D输入的灵敏度:101 = 500mv;202 = 1v;303 = 1.5v;404 = 2v;505 = 2.5v;1010 = 5v(最大)。 #define tunestep A0 //调谐步骤按钮使用的引脚。 #define band A1 //波段选择器按钮使用的引脚。 #define rx_tx A2 // RX / TX选择器开关使用的引脚,RX =开关断开,TX =开关闭合至GND。在TX中时,不考虑IF。 #define adc A3 //信号表A / D输入所使用的引脚。
### Si5351芯片驱动程序与开发指南 Si5351是一款高性能、多输出的时钟发生器,广泛应用于需要高精度时钟信号的嵌入式系统中。该芯片支持通过I2C接口进行通信和配置,适用于多种微控制器平台,包括STM32系列。 对于Si5351的驱动开发,通常需要完成以下关键步骤: - **初始化I2C接口**:确保微控制器与Si5351之间的通信正常工作。这包括设置I2C的时钟频率、配置引脚复用功能等。 - **寄存器编程**:Si5351的配置主要通过写入特定寄存器来完成。例如,`CLK0_CTRL`、`CLK1_CTRL` 和 `CLK2_CTRL` 寄存器用于控制各个时钟输出的使能状态和源选择。此外,`PLL_SOURCE` 寄存器用于选择锁相环(PLL)的输入源[^2]。 - **时钟输出配置**:根据应用需求,可以配置每个时钟输出的频率和相位。Si5351支持多个独立的时钟输出,并且可以通过软件动态调整这些参数。常见的配置包括设置分频系数、选择PLL源以及启用或禁用特定的时钟输出[^2]。 - **电源管理与错误处理**:为了确保系统的稳定性和可靠性,驱动程序还需要处理电源管理和错误检测机制。例如,在系统启动时检查Si5351的状态寄存器以确认设备是否正确初始化;在运行过程中监控可能出现的错误并采取相应的恢复措施[^2]。 关于具体的驱动程序实现,您可以参考开源项目中的示例代码。例如,GitHub上有许多针对不同微控制器平台的Si5351驱动程序实现,其中包括基于STM32F4xx系列微控制器的驱动开发案例。这些项目不仅提供了完整的驱动代码,还包含了详细的文档说明,帮助开发者快速上手和集成到自己的项目中[^2]。 如果您正在使用的是STM32F4xx系列微控制器,那么可以参考以下简化的驱动框架: ```c // I2C初始化函数 void si5351_i2c_init(void) { // 配置I2C GPIO // 初始化I2C外设 } // 写入Si5351寄存器 void si5351_write_register(uint8_t reg, uint8_t value) { // 使用I2C发送数据到指定寄存器 } // 读取Si5351寄存器 uint8_t si5351_read_register(uint8_t reg) { // 从指定寄存器读取数据 return 0; // 返回读取的数据 } // 配置时钟输出 void si5351_configure_clock(uint8_t clk_num, uint32_t freq) { // 计算分频系数 // 设置相关寄存器 } ``` 以上代码仅为示意性质,实际开发中需要根据具体硬件平台和应用需求进行适当的修改和完善。 至于更详细的官方开发指南,建议访问Silicon Labs官方网站下载最新的Si5351数据手册和技术文档。这些资料详细介绍了芯片的功能特性、电气参数以及推荐的设计实践,是进行驱动开发不可或缺的重要参考资料[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值