步进电机整步与半步驱动 (状态机下运行)附源代码(包括注释)与protues硬件原理图和protues仿真结果(非常详细)

前话:本文的所有程序与仿真,均以硬件的形式运行过,实际电机运行的效果图或者视频就没有贴出来了。硬件采用以DSPIC33FJ32MC204为核心的驱动电路。

注1:因为步进电机存在低频振荡,所以我们选择的运行速度稍微大一点(这个速度在其他永磁同步电机,无刷直流电机中也算是比较低的速度),速度选择为200RPM,程序中定时器定时结束进入中断的时间与这个速度息息相关。

注2:程序中注释部分可能有所纰漏或者不对,但是代码是正常的,因为注释是我在调试过程中随手记下来的,只能用作参考,不能完全相信。

注3:步进电机整步与半步相关原理可以看我之前出的两篇文章dspic33FJ32MC204开发板驱动步进电机原理(2)

dspic33FJ32MC204开发板驱动步进电机原理(1)

注4:关于Proteus仿真,有个很重要的点,那就是Proteus没办法切换时钟源,如果while等待切换时钟源,那么程序会卡死,因为没有办法成功切换时钟源。所以必须把切换时钟源的代码部分注释,默认配置程序中的FRC时钟源进行仿真。

程序思路:

首先当然是初始化外设(IO口,定时器,中断,PWM,时钟源)

然后整步与半步是查表运行每一步,所以我们分别定义整步与半步的数组,用来存放驱动步序。

步序可以看我之前的两篇文章,也可参阅其他资料看看整步与半步运行原理。

unsigned int FullStep[2][4] = {{0x002A,0x0025,0x0015,0x001A},{0x0002,0x0002,0x0001,0x0001}};
unsigned int HalfStep[2][8] = {{0x000A,0x002A,0x0020,0x0025,0x0005,0x0015,0x0010,0x001A},{0x0000,0x0002,0x0001}};

数组里面的数据与P1OVDCON和P2OVDCON两个改写控制寄存器还有步序有关。

采用定时器1产生中断,在定时中断程序中查表切换步序。定时器定时时间与电机运行的速度RPM有关。时间的计算也同样参考我之前的两篇文章。

状态机设置三个状态,初始状态,启动状态,停止状态,三个状态由一个按键负责,采用if-else语句切换判断。当然我们也需要加入按键消抖程序(虽然硬件电路有电容消抖,但我们还是插入按键消抖程序)。

整步程序:

#include "p33FJ32MC204.h"
#include "dsp.h"
#include <xc.h>
#include <PPS.H>

/*****************Config bit settings* ***************/
// FBS
#pragma config BWRP = WRPROTECT_OFF     // Boot Segment Write Protect (Boot Segment may be written)
#pragma config BSS = NO_FLASH           // Boot Segment Program Flash Code Protection (No Boot program Flash segment)

// FGS
#pragma config GWRP = OFF               // General Code Segment Write Protect (User program m++emory is not write-protected)
#pragma config GSS = OFF                // General Segment Code Protection (User program memory is not code-protected)

// FOSCSEL
#pragma config FNOSC = FRC              //改了 初始振荡源选择FRC Oscillator Mode (Internal Fast RC (FRC) with divide by N)
#pragma config IESO = ON                // 使用内部FRC振荡源启动器件,然后自动切换为就绪的用户选择的振荡器源 Internal External Switch Over Mode (Start-up device with FRC, then automatically switch to user-selected oscillator source when ready)

// FOSC
#pragma config POSCMD = XT              //改了 Primary Oscillator Source (Primary Oscillator Disabled)
#pragma config OSCIOFNC = OFF           // OSC2 Pin Function (OSC2 pin has clock out function)
#pragma config IOL1WAY = OFF            // 改了 外设引脚选择配置位 允许多次配置 Peripheral Pin Select Configuration (Allow Only One Re-configuration)
#pragma config FCKSM = CSECME           // 改了 必须要使得时钟使能 Clock Switching and Monitor (Both Clock Switching and Fail-Safe Clock Monitor are disabled)

// FWDT
#pragma config FWDTEN = OFF             // 改了 把看门狗关闭了 Watchdog Timer Enable (Watchdog timer always enabled)

// FPOR
#pragma config FPWRT = PWR128           // POR Timer Value (128ms)
#pragma config ALTI2C = OFF             // Alternate I2C  pins (I2C mapped to SDA1/SCL1 pins)
#pragma config LPOL = ON                // Motor Control PWM Low Side Polarity bit (PWM module low side output pins have active-high output polarity)
#pragma config HPOL = ON                // Motor Control PWM High Side Polarity bit (PWM module high side output pins have active-high output polarity)
#pragma config PWMPIN = ON              // Motor Control PWM Module Pin Mode bit (PWM module pins controlled by PORT register at device Reset)

// FICD
#pragma config ICS = PGD3               // Comm Channel Select (Communicate on PGC1/EMUC1 and PGD1/EMUD1)
#pragma config JTAGEN = OFF             // JTAG Port Enable (JTAG is Disabled)

void Clock_InitSet()
{
            //Perform a clock switch to 40MIPS (80Mhz) 外部晶振电路图上是7.3728MHZ(FIN)但是实际板子上是8MHZ
	PLLFBD = 38;			//M=PLLFBD+2       = 40
	CLKDIVbits.PLLPOST = 0;	//N2=2(PLLPOST+1)  = 2
	CLKDIVbits.PLLPRE = 0;	//N1=PLLPRE+2      = 2

	//unlock OSCCON register
	__builtin_write_OSCCONH(0x03);//切换到带PLL的主振荡器模式   经过测试 应该切换成功了
	__builtin_write_OSCCONL(0x01);//时钟切换被使能,外设引脚未锁定,允许写入外设引脚选择寄存器,PLL处于失锁状态,禁止辅助振荡器,请求切换新的振荡器
    //wait for clock to stabilize
	//while(OSCCONbits.COSC != 0b011);
	//wait for PLL to lock
	//while(OSCCONbits.LOCK != 1) {};
    //clock switch finished
}


void PWM_InitSet()
{
        //PWM配置
    //PWM工作模式
    P1TCON = 0x0000;//PWM时基在占空比双重更新的递增递减运行模式下工作,PWM时基在空闲模式下运行,预分频比1:1,后分频比1:1
    P2TCON = 0x0000;

    P1TPER = 1999;//计数周期 PWM输出10KHZ    如果没有注释计数周期这两行,测得第一个LED周期为6ms,对应200rpm。注释后周期不为6ms,有时在5.6ms,有时在6.5ms,导致电机振荡和噪音很大。
    P2TPER = 1999;

    //MCPWM模块占空比初始化
    P1DC1 = 2000;
    P1DC2 = 2000;
    P1DC3 = 2000;
    P2DC1 = 2000;

    //MCPWM模块输出模式选择
    PWM1CON1 = 0x0077;//互补输出模式,引脚用于PWM输出
    PWM1CON2 = 0x0002;//特殊事件触发信号后分频比1:1,更新有效占空比寄存器与PWM时基同步,通过PXOVDCON寄存器进行的输出改写与PWM时基同步   这里的输出改写比较重要

    PWM2CON1 = 0x0011;//互补输出模式
    PWM2CON2 = 0x0002;//特殊事件触发信号后分频比1:1,更新有效占空比寄存器与PWM时基同步,通过PXOVDCON寄存器进行的输出改写与PWM时基同步

    //死区插入(仅限互补PWM输出模式)
    P1DTCON1bits.DTAPS = 2;// Clck DeadTime = 4 Tcy
    P1DTCON1bits.DTA = 5;//Dead time = 5*4*25ns = 500ns
    P2DTCON1bits.DTAPS = 2;
    P2DTCON1bits.DTA = 5;//设定A死区值


    P1DTCON2 = 0;//死区都由死区单元A提供
    P2DTCON2 = 0;

    //改写PWM1,PWM2初始化引脚为无效状态
    P2OVDCON = 0;
    P1OVDCON = 0;
    
    //PWM1中断
    IFS3bits.PWM1IF = 0;
    IEC3bits.PWM1IE = 0;
    IPC14bits.PWM1IP = 2;

    //PWM2中断
    IFS4bits.PWM2IF = 0;
    IEC4bits.PWM2IE = 0;
    IPC18bits.PWM2IP = 2;
    
    //失能PWM1,PWM2
    P1TCONbits.PTEN = 0;
    P2TCONbits.PTEN = 0;
}

void timer1_InitSet()
{
     //Timer 1 中断设置
//         SRbits.IPL=0b010;
//         CORCONbits.IPL3=0;
//         INTCON1bits.NSTDIS = 0;//0允许中断嵌套,1禁止中断嵌套 默认是允许的
//         INTCON2bits.ALTIVT = 0;
         IFS0bits.T1IF = 0; 		// Clear Timer1 Interrupt Flag
         IEC0bits.T1IE = 1; 		// Enable Timer1 interrupt
         IPC0bits.T1IP = 3; 		// Set Timer 1 Interrupt Priority Level

         //TMR1设置,延时2.5ms
         TMR1 = 0;// Resetting TIMER
         T1CONbits.TGATE = 0;  //disable gated timer mode
         T1CON = 0x0000;			// reset timer configuration
         PR1 = 7499;                       //load the period value   FRC的值5786  PLL_XT的值62499
         T1CONbits.TCKPS = 1;	// 1 预分频比为1:8
       //return 0;
}

//函数声明段
void StateMachine();


//变量定义段
//120rpm 整步 step1-step4 每步计算得到Timer1中断触发时间为2.5ms
//查表P1OVDCON,P2OVDCON
unsigned int FullStep[2][4] = {{0x002A,0x0025,0x0015,0x001A},{0x0002,0x0002,0x0001,0x0001}};
int i=0;
int n;
int stepcount = 0;
int state;
int statecopy;
int buttonCounter;
int BUTTON_FILTER = 20;
int buttonflag = 0;
//宏定义段
#define BUTTON_PIN  PORTBbits.RB7   //按键引脚BTN 
//State machine defines
#define STATE_OFF      0
#define STATE_RUN      1
#define STATE_INIT     2

int main(void)
{
    //TRISCbits.TRISC3 = 0;
    //TRISCbits.TRISC9 = 0;
    
    //程序初始化
    Clock_InitSet();
    PWM_InitSet();
    timer1_InitSet();
    
    //设置状态机状态
    state = STATE_INIT;
    
    while(1)//原地踏步
	{
        //read buttonCounter state and debounce
		if((BUTTON_PIN == 0) && (buttonCounter <= BUTTON_FILTER))
		{
			//一直做消除抖动计数,确保按键按下
			buttonCounter ++;
		}
        if(buttonflag == 1)
        {
            state = STATE_RUN;
        }
        else
        {
            state = STATE_OFF;
        }
		//call state machine
		StateMachine();
	}
    
}


void StateMachine(void)
{
    //状态处于启动
    if(state == STATE_RUN)
    {
        if( buttonCounter > BUTTON_FILTER)	//read buttonCounter state
        {
            if(BUTTON_PIN == 1)
            {   
                buttonCounter = 0;
                buttonflag=!buttonflag;
                //使能PWM1,PWM2
                P1TCONbits.PTEN = 1;
                P2TCONbits.PTEN = 1;
                T1CONbits.TON = 1;  // Enable Timer1 定时器开启了就一直在工作,除了休眠和空闲模式
                
            }    

        }
    }
    else
    //状态处于停止
	if (state==STATE_OFF)           
	{
		if( buttonCounter > BUTTON_FILTER)
		{	
			if(BUTTON_PIN == 1)             
			{
    		    buttonCounter = 0;
                buttonflag=!buttonflag;
                //失能PWM
                P1TCONbits.PTEN = 0;
                P2TCONbits.PTEN = 0;
                P2OVDCON = 0;
                P1OVDCON = 0;
                T1CONbits.TON = 0;  // Enable Timer1 定时器开启了就一直在工作,除了休眠和空闲模式 	
                
			}
		}
		
	} 
//end of the OFF state  
    else
//状态处于初始化
    if (state==STATE_INIT)      //init state
	{
		statecopy = state;          //sync variables    
	}
//end of the INIT state
}

void __attribute__((__interrupt__,no_auto_psv)) _T1Interrupt(void)
{
    if(stepcount < 4)
    {
        switch(stepcount)
        {
            case 0: P1OVDCON = FullStep[0][0];P2OVDCON = FullStep[1][0];break;//A+,B+
            case 1: P1OVDCON = FullStep[0][1];P2OVDCON = FullStep[1][1];break;//B+,A-
            case 2: P1OVDCON = FullStep[0][2];P2OVDCON = FullStep[1][2];break;//A-,B-
            case 3: P1OVDCON = FullStep[0][3];P2OVDCON = FullStep[1][3];break;//B-,A+

        }
        stepcount++;
    }
    else
    {
        stepcount = 0;
        
        P1OVDCON = FullStep[0][0];P2OVDCON = FullStep[1][0];

        stepcount++;
    }
    IFS0bits.T1IF = 0;
}


/* PWM1 Interrupt */
void __attribute__((__interrupt__,auto_psv)) _MPWM1Interrupt(void)
{
    IFS3bits.PWM1IF = 0;/* Timer 1 Interrupt */   //下面这段程序运行了
}

/* PWM2 Interrupt */
void __attribute__((__interrupt__,auto_psv)) _MPWM2Interrupt(void)
{
    IFS4bits.PWM2IF = 0;
}

半步程序:

#include "p33FJ32MC204.h"
#include "dsp.h"
#include <xc.h>
#include <PPS.H>

/*****************Config bit settings* ***************/
// FBS
#pragma config BWRP = WRPROTECT_OFF     // Boot Segment Write Protect (Boot Segment may be written)
#pragma config BSS = NO_FLASH           // Boot Segment Program Flash Code Protection (No Boot program Flash segment)

// FGS
#pragma config GWRP = OFF               // General Code Segment Write Protect (User program m++emory is not write-protected)
#pragma config GSS = OFF                // General Segment Code Protection (User program memory is not code-protected)

// FOSCSEL
#pragma config FNOSC = FRC              //改了 初始振荡源选择FRC Oscillator Mode (Internal Fast RC (FRC) with divide by N)
#pragma config IESO = ON                // 使用内部FRC振荡源启动器件,然后自动切换为就绪的用户选择的振荡器源 Internal External Switch Over Mode (Start-up device with FRC, then automatically switch to user-selected oscillator source when ready)

// FOSC
#pragma config POSCMD = XT              //改了 Primary Oscillator Source (Primary Oscillator Disabled)
#pragma config OSCIOFNC = OFF           // OSC2 Pin Function (OSC2 pin has clock out function)
#pragma config IOL1WAY = OFF            // 改了 外设引脚选择配置位 允许多次配置 Peripheral Pin Select Configuration (Allow Only One Re-configuration)
#pragma config FCKSM = CSECME           // 改了 必须要使得时钟使能 Clock Switching and Monitor (Both Clock Switching and Fail-Safe Clock Monitor are disabled)

// FWDT
#pragma config FWDTEN = OFF             // 改了 把看门狗关闭了 Watchdog Timer Enable (Watchdog timer always enabled)

// FPOR
#pragma config FPWRT = PWR128           // POR Timer Value (128ms)
#pragma config ALTI2C = OFF             // Alternate I2C  pins (I2C mapped to SDA1/SCL1 pins)
#pragma config LPOL = ON                // Motor Control PWM Low Side Polarity bit (PWM module low side output pins have active-high output polarity)
#pragma config HPOL = ON                // Motor Control PWM High Side Polarity bit (PWM module high side output pins have active-high output polarity)
#pragma config PWMPIN = ON              // Motor Control PWM Module Pin Mode bit (PWM module pins controlled by PORT register at device Reset)

// FICD
#pragma config ICS = PGD3               // Comm Channel Select (Communicate on PGC1/EMUC1 and PGD1/EMUD1)
#pragma config JTAGEN = OFF             // JTAG Port Enable (JTAG is Disabled)

void Clock_InitSet()
{
            //Perform a clock switch to 40MIPS (80Mhz) 外部晶振电路图上是7.3728MHZ(FIN)但是实际板子上是8MHZ
	PLLFBD = 38;			//M=PLLFBD+2       = 40
	CLKDIVbits.PLLPOST = 0;	//N2=2(PLLPOST+1)  = 2
	CLKDIVbits.PLLPRE = 0;	//N1=PLLPRE+2      = 2

	//unlock OSCCON register
	__builtin_write_OSCCONH(0x03);//切换到带PLL的主振荡器模式   经过测试 应该切换成功了
	__builtin_write_OSCCONL(0x01);//时钟切换被使能,外设引脚未锁定,允许写入外设引脚选择寄存器,PLL处于失锁状态,禁止辅助振荡器,请求切换新的振荡器
    //wait for clock to stabilize
	//while(OSCCONbits.COSC != 0b011);
	//wait for PLL to lock
	//while(OSCCONbits.LOCK != 1) {};
    //clock switch finished
}


void PWM_InitSet()
{
        //PWM配置
    //PWM工作模式
    P1TCON = 0x0000;//PWM时基在占空比双重更新的递增递减运行模式下工作,PWM时基在空闲模式下运行,预分频比1:1,后分频比1:1
    P2TCON = 0x0000;

    P1TPER = 1999;//计数周期 PWM输出10KHZ    如果没有注释计数周期这两行,测得第一个LED周期为6ms,对应200rpm。注释后周期不为6ms,有时在5.6ms,有时在6.5ms,导致电机振荡和噪音很大。
    P2TPER = 1999;

    //MCPWM模块占空比初始化
    P1DC1 = 2000;
    P1DC2 = 2000;
    P1DC3 = 2000;
    P2DC1 = 2000;

    //MCPWM模块输出模式选择
    PWM1CON1 = 0x0077;//互补输出模式,引脚用于PWM输出
    PWM1CON2 = 0x0002;//特殊事件触发信号后分频比1:1,更新有效占空比寄存器与PWM时基同步,通过PXOVDCON寄存器进行的输出改写与PWM时基同步   这里的输出改写比较重要

    PWM2CON1 = 0x0011;//互补输出模式
    PWM2CON2 = 0x0002;//特殊事件触发信号后分频比1:1,更新有效占空比寄存器与PWM时基同步,通过PXOVDCON寄存器进行的输出改写与PWM时基同步

    //死区插入(仅限互补PWM输出模式)
    P1DTCON1bits.DTAPS = 2;// Clck DeadTime = 4 Tcy
    P1DTCON1bits.DTA = 5;//Dead time = 5*4*25ns = 500ns
    P2DTCON1bits.DTAPS = 2;
    P2DTCON1bits.DTA = 5;//设定A死区值


    P1DTCON2 = 0;//死区都由死区单元A提供
    P2DTCON2 = 0;

    //改写PWM1,PWM2初始化引脚为无效状态
    P2OVDCON = 0;
    P1OVDCON = 0;
    
    //PWM1中断
    IFS3bits.PWM1IF = 0;
    IEC3bits.PWM1IE = 0;
    IPC14bits.PWM1IP = 2;

    //PWM2中断
    IFS4bits.PWM2IF = 0;
    IEC4bits.PWM2IE = 0;
    IPC18bits.PWM2IP = 2;
    
    //失能PWM1,PWM2
    P1TCONbits.PTEN = 0;
    P2TCONbits.PTEN = 0;
}

void timer1_InitSet()
{
     //Timer 1 中断设置
//         SRbits.IPL=0b010;
//         CORCONbits.IPL3=0;
//         INTCON1bits.NSTDIS = 0;//0允许中断嵌套,1禁止中断嵌套 默认是允许的
//         INTCON2bits.ALTIVT = 0;
         IFS0bits.T1IF = 0; 		// Clear Timer1 Interrupt Flag
         IEC0bits.T1IE = 1; 		// Enable Timer1 interrupt
         IPC0bits.T1IP = 3; 		// Set Timer 1 Interrupt Priority Level

         //TMR1设置,延时2.5ms
         TMR1 = 0;// Resetting TIMER
         T1CONbits.TGATE = 0;  //disable gated timer mode
         T1CON = 0x0000;			// reset timer configuration
         PR1 = 3499;                //半步运行200rpm是3499
         T1CONbits.TCKPS = 1;	// 1 预分频比为1:8
       //return 0;
}

//函数声明段
void StateMachine();


//变量定义段
//120rpm 整步 step1-step4 每步计算得到Timer1中断触发时间为2.5ms
//查表P1OVDCON,P2OVDCON
unsigned int HalfStep[2][8] = {{0x000A,0x002A,0x0020,0x0025,0x0005,0x0015,0x0010,0x001A},{0x0000,0x0002,0x0001}};
int i=0;
int n;
int stepcount = 0;
int state;
int statecopy;
int buttonCounter;
int BUTTON_FILTER = 20;
int buttonflag = 0;
//宏定义段
#define BUTTON_PIN  PORTBbits.RB7   //按键引脚BTN 
//State machine defines
#define STATE_OFF      0
#define STATE_RUN      1
#define STATE_INIT     2

int main(void)
{
    //TRISCbits.TRISC3 = 0;
    //TRISCbits.TRISC9 = 0;
    
    //程序初始化
    Clock_InitSet();
    PWM_InitSet();
    timer1_InitSet();
    
    //设置状态机状态
    state = STATE_INIT;
    
    while(1)//原地踏步
	{
        //read buttonCounter state and debounce
		if((BUTTON_PIN == 0) && (buttonCounter <= BUTTON_FILTER))
		{
			//一直做消除抖动计数,确保按键按下
			buttonCounter ++;
		}
        if(buttonflag == 1)
        {
            state = STATE_RUN;
        }
        else
        {
            state = STATE_OFF;
        }
		//call state machine
		StateMachine();
	}
    
}


void StateMachine(void)
{
    //状态处于启动
    if(state == STATE_RUN)
    {
        if( buttonCounter > BUTTON_FILTER)	//read buttonCounter state
        {
            if(BUTTON_PIN == 1)
            {   
                buttonCounter = 0;
                buttonflag=!buttonflag;
                //使能PWM1,PWM2
                P1TCONbits.PTEN = 1;
                P2TCONbits.PTEN = 1;
                T1CONbits.TON = 1;  // Enable Timer1 定时器开启了就一直在工作,除了休眠和空闲模式
                
            }    

        }
    }
    else
    //状态处于停止
	if (state==STATE_OFF)           
	{
		if( buttonCounter > BUTTON_FILTER)
		{	
			if(BUTTON_PIN == 1)             
			{
    		    buttonCounter = 0;
                buttonflag=!buttonflag;
                //失能PWM
                P1TCONbits.PTEN = 0;
                P2TCONbits.PTEN = 0;
                P2OVDCON = 0;
                P1OVDCON = 0;
                T1CONbits.TON = 0;  // Enable Timer1 定时器开启了就一直在工作,除了休眠和空闲模式 	
                
			}
		}
		
	} 
//end of the OFF state  
    else
//状态处于初始化
    if (state==STATE_INIT)      //init state
	{
		statecopy = state;          //sync variables    
	}
//end of the INIT state
}

void __attribute__((__interrupt__,no_auto_psv)) _T1Interrupt(void)
{
    if(stepcount < 8)
    {
        switch(stepcount)
        {
            case 0: P1OVDCON = HalfStep[0][0];P2OVDCON = HalfStep[1][0];break;//A+
            case 1: P1OVDCON = HalfStep[0][1];P2OVDCON = HalfStep[1][1];break;//A+,B+
            case 2: P1OVDCON = HalfStep[0][2];P2OVDCON = HalfStep[1][1];break;//B+
            case 3: P1OVDCON = HalfStep[0][3];P2OVDCON = HalfStep[1][1];break;//B+,A-
            case 4: P1OVDCON = HalfStep[0][4];P2OVDCON = HalfStep[1][0];break;//A-
            case 5: P1OVDCON = HalfStep[0][5];P2OVDCON = HalfStep[1][2];break;//A-,B-
            case 6: P1OVDCON = HalfStep[0][6];P2OVDCON = HalfStep[1][2];break;//B-
            case 7: P1OVDCON = HalfStep[0][7];P2OVDCON = HalfStep[1][2];break;//B-,A+

        }
        stepcount++;
    }
    else
    {
        stepcount = 0;
        
        P1OVDCON = HalfStep[0][0];P2OVDCON = HalfStep[1][0];//A+

        stepcount++;
    }
    IFS0bits.T1IF = 0;
}


/* PWM1 Interrupt */
void __attribute__((__interrupt__,auto_psv)) _MPWM1Interrupt(void)
{
    IFS3bits.PWM1IF = 0;/* Timer 1 Interrupt */   //下面这段程序运行了
}

/* PWM2 Interrupt */
void __attribute__((__interrupt__,auto_psv)) _MPWM2Interrupt(void)
{
    IFS4bits.PWM2IF = 0;
}

Proteus仿真原理图:

仿真结果:可以看到电机在以整步或者半步运行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

清漠233

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

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

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

打赏作者

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

抵扣说明:

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

余额充值