STM32个人学习笔记(标准库)-2023.09.20

第六节、TIM定时中断

6.1 TIM简介

TIM(Timer)定时器

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断(定时触发中断)。

16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时。

不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能。

根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型。

定时器的类型如下:

STM32F103ZET6定时器资源:TIM1-8均有。

主模式触发DAC:在使用DAC时,可以将定时器的更新事件映射到触发输出TRGO上,TRGO直接接到DAC的触发转换引脚上,定时器的更新就不需要通过中断来触发DAC转换。实现了硬件的自动化。

6.2 定时器结构图分析

首先来看基本定时器:

它们的功能如下:

预分频器:对72MHZ的计数时钟进行预分频,例如如果预分频器写1,则为2分频,输出频率=输入频率/2=36MHZ。(即实际分配系数=预分频器的值+1

计数器:对预分频后的技术时钟进行计数。

自动重装寄存器:16位的寄存器,存入写入的计数目标。运行过程中,计数值不断自增,当计数值等于自动重装值,计时时间到,产生中断信号(更新中断,更新中断之后通往NVIC,配置好NVIC的定时器中断后,定时器的更新中断就能得到CPU的响应),并且清零计数器。

接下来来看通用定时器:

通用定时器与高级定时器支持三种计数模式,即向上计数、向下计数、中央对齐计数。

通用定时器的电路总共可以分为四个部分:

1、时基单元:与基本定时器的时基单元相同。

2、内外时钟源选择和主从触发模式:通用定时器不仅可以选择内部时钟作为时钟源,也可以选贼外部时钟作为时钟源。

3、输入捕获电路:可以用于测量方波频率等。

4、输出比较电路:总共有四个通道,分别对应CH1到CH4的引脚,适用于PWM产生波形。

下面再来看一下高级定时器:

相较于通用定时器,高级定时器多了个重复计数器,使计数时间大大增加。并且高级计数器还拥有输出比较模块,驱动三相无刷电机。以及刹车输入功能。

6.3 定时中断的基本结构

通过上图,定时器初始化可以分为以下几个步骤:

1、RCC开启时钟。

2、选择时基单元的时钟源。

3、配置时基单元。

4、配置输出中断控制。运行更新中断输出到NVIC

5、配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级。

6、运行控制。使能计数器。

通过时序图,我们能研究时基单元运行时的细节问题。

1、预分频器时序

计数器计数频率:CK_CNT=CK_PSC/(PSC+1)

预分频器的工作流程:首先,CK_PSC(预分频器输入时钟)不断运行;CNT_EN(计数器使能)为高电平时计数器正常运行,低电平时计数器停止;CK_CNT(计数器时钟)运行时,根据分频系数,前半段预分频器参数为1,计数器时钟等于预分频器前的时钟,后半段预分频器系数变为2,计数器时钟变为预分频器前时钟的一半。在计数器时钟的驱动下,计数器寄存器也跟随时钟的上升沿不断自增,在FC后,计数值变为0,下面产生更新事件。

2、计数器时序

计数器溢出频率:CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)

3、RCC时钟树

 

6.4 代码实现定时器功能

首先来看一下定时器的库函数:

void TIM_DeInit(TIM_TypeDef* TIMx);//恢复缺省配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//时基单元初始化
/*配置输出比较模块*/
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
/*结构体配置输入捕获单元的函数*/
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
/*初始化输入捕获单元*/
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

void TIM_BDTRConfig(TIM_TypeDef* TIMx, TIM_BDTRInitTypeDef *TIM_BDTRInitStruct);
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//给结构体变量赋默认值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);//给结构体变量赋参数值
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);//给输入捕获结构体赋一个初值
void TIM_BDTRStructInit(TIM_BDTRInitTypeDef* TIM_BDTRInitStruct);
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);//使能计数器
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//使能外设中断输出
void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource);
void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength);
void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState);
/*时基单元时钟选择*/
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                   uint16_t ExtTRGFilter);

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//可用于单独写预分频值
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//改变计数器的计数模式
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择输入触发源TRGI
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
                                uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);//定时器编码器接口配置
/*配置强制输出模式*/
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);//自动重装器预装功能配置
void TIM_SelectCOM(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectCCDMA(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_CCPreloadControl(TIM_TypeDef* TIMx, FunctionalState NewState);
/*配置OC预装功能*/
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
/*配置快速使能*/
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
/*外部事件时清除REF信号*/
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
/*单独设置输出比较极性*/
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
/*单独修改输出使能参数*/
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);
/*选择输出比较模式*/
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);

void TIM_UpdateDisableConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_UpdateRequestConfig(TIM_TypeDef* TIMx, uint16_t TIM_UpdateSource);
void TIM_SelectHallSensor(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode);
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);//选择输出触发源TRGO
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);//选择从模式
void TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode);
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);//给计数器写入一个计数值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);//给自动重装器写入一个值
/*单独更改CCR寄存器的值*/
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
/*分别单独配置通道1,2,3,4的分频器*/
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetClockDivision(TIM_TypeDef* TIMx, uint16_t TIM_CKD);
/*分别读取四个通道的CCR*/
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);

uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);//获取当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);//获取当前的预分频器的值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

内部时钟实现定时器定时:

Timer.c:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	/*RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	/*选择时基单元的时钟*/
	TIM_InternalClockConfig(TIM2);
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef Tim_InitStructure;
	Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
	Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
	Tim_InitStructure.TIM_Period=10000-1;//选择周期-ARR值
	Tim_InitStructure.TIM_Prescaler=7200-1;//预分频器
	Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
	TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
	/*配置输出中断控制,使能更新中断*/
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	/*配置NVIC*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStructure);
	/*启动定时器*/
	TIM_Cmd(TIM2,ENABLE);
}

/*
中断函数
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) ==SET )
	{
		
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}*/

 Timer.h:

#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif

main函数:

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Delay.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
	OLED_Init();
	Timer_Init();
	OLED_ShowString(1,1,"Num:");	
	while(1)
	{
	OLED_ShowNum(1,5,Num,5);
	}
}

/*中断函数*/
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) ==SET )
	{
	Num++;
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

 定时器外部时钟:

Timer.c:

#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	/*RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	/*选择时基单元的时钟*/
	TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0111);//通过ETR引脚的外部时钟模式2配置
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef Tim_InitStructure;
	Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
	Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
	Tim_InitStructure.TIM_Period=10-1;//选择周期-ARR值
	Tim_InitStructure.TIM_Prescaler=1-1;//预分频器
	Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
	TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
	/*配置输出中断控制,使能更新中断*/
	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	/*配置NVIC*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStructure);
	/*启动定时器*/
	TIM_Cmd(TIM2,ENABLE);
}

/*计数器值封装*/
uint16_t Timer_GetCount(void)
{
	return TIM_GetCounter(TIM2);
}

/*
中断函数
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) ==SET )
	{
		
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}*/

 Timer.h:

#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
uint16_t Timer_GetCount(void);
#endif

main函数:

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Delay.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
	OLED_Init();
	Timer_Init();
	OLED_ShowString(1,1,"Num:");
	OLED_ShowString(2,1,"CNT:");	
	while(1)
	{
	OLED_ShowNum(1,5,Num,5);
	OLED_ShowNum(2,5,Timer_GetCount(),5);
	}
}

/*中断函数*/
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_IT_Update) ==SET )
	{
	Num++;
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
}

 6.5 TIM输出比较

6.5.1 输出比较简介

OC(Output Compare)输出比较

输出比较可以通过比较CNT(时基单元计数器)与CCR寄存器(捕获/比较寄存器)值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形

每个高级定时器和通用定时器都拥有4个输出比较通道

高级定时器的前3个通道额外拥有死区生成和互补输出的功能

6.5.2 PWM简介

PWM(Pulse Width Modulation)脉冲宽度调制

在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域。使用PWM波形等效实现模拟信号的输出。

PWM参数:      频率 = 1 / TS            占空比 = TON / TS           分辨率 = 占空比变化步距

通用定时器的输出比较电路:

输出比较模式:

PWM的基本结构:

 通过上图,PWM进行初始化可分为以下几步:

1、RCC开启时钟。

2、配置时基单元。

3、配置输出比较单元。

4、配置GPIO。

5、运行控制,启动计数器。

PWM的参数计算:

 上图中,蓝色:CNT,黄色:ARR,红色:CCR

PWM的频率=计数器的更新频率

PWM的占空比=30/100=30%

6.5.3 舵机简介

舵机是一种根据输入PWM信号占空比来控制输出角度的装置

输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms

6.5.4 直流电机及驱动介绍

直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转

直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作

TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向

电机驱动电路的电路图如下:

通过控制IN1,IN2两个引脚的电平,能够控制电机是正转还是反转。 

6.6 代码实现TIM功能

PWM驱动LED呼吸灯:

PWM.c:

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	/*RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//只有选择复用推挽输出,引脚的控制权才能转交给片上外设
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	/*选择时基单元的时钟*/
	TIM_InternalClockConfig(TIM2);
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef Tim_InitStructure;
	Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
	Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
	Tim_InitStructure.TIM_Period=100-1;//选择周期-ARR值
	Tim_InitStructure.TIM_Prescaler=720-1;//预分频器-PSC值
	Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
	TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
	/*配置输出比较单元*/
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//给结构体成员赋初始值
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出状态
	TIM_OCInitStructure.TIM_Pulse=0;//设置CCR寄存器的值
	TIM_OC2Init(TIM2,&TIM_OCInitStructure);
	/*启动定时器*/
	TIM_Cmd(TIM2,ENABLE);
}
/*封装设定CCR值函数*/
void PWM_SetCompare2(uint16_t Compare)
{
	 TIM_SetCompare2(TIM2,Compare);
}

PWM.h:

#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);
#endif

main函数:

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Delay.h"
#include "PWM.h"
uint8_t i;
int main(void)
{
	OLED_Init();
	PWM_Init();
	while(1)
	{
	for(i=0;i<=100;i++)
		{
		PWM_SetCompare2(i);
		Delay_ms(10);
		}
	for(i=0;i<=100;i++)
		{
		PWM_SetCompare2(100-i);
		Delay_ms(10);
		}
	}
}

按键控制舵机:

Servo.c:

#include "stm32f10x.h"                  // Device header
#include "PWM.h"

void Servo_Init(void)
{
	PWM_Init();
}

/*舵机的设定角度函数
0   500
180	2500
*/
void Servo_SetAngle(float Angle)
{
	PWM_SetCompare2(Angle/180*2000+500);
}

Servo.h:

#ifndef __SERVO_H
#define __SERVO_H
void Servo_Init(void);
void Servo_SetAngle(float Angle);
#endif

main函数:

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Delay.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum;
float Angle;
int main(void)
{
	OLED_Init();
	Servo_Init();
	Key_Init();
	OLED_ShowString(1,1,"Angle:");
	while(1)
	{
	
	KeyNum=Key_GetNum();
	if(KeyNum == 1)
	{
	Angle+=30;
	if(Angle>180)
	{
	Angle=0;
	}
	}
	Servo_SetAngle(Angle);
	OLED_ShowNum(1,7,Angle,3);
	}
}

按键控制直流电机:

Motor.c:

#include "stm32f10x.h"                  // Device header
#include "PWM.h"
void Motor_Init(void)
{
	/*初始化电机方向控制脚*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	PWM_Init();
}

void Motor_SetSpeed(int8_t Speed)
{
	if(Speed>=0)
	{
	GPIO_SetBits(GPIOA,GPIO_Pin_4);
	GPIO_ResetBits(GPIOA,GPIO_Pin_5);
	PWM_SetCompare2(Speed);
	}
	else
	{
	GPIO_SetBits(GPIOA,GPIO_Pin_5);
	GPIO_ResetBits(GPIOA,GPIO_Pin_4);
	PWM_SetCompare2(-Speed);
	}
}
	

motor.h:

#ifndef __MOTOR_H
#define __MOTOR_H
void Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);
#endif

main函数:

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Delay.h"
#include "MOTOR.h"
#include "Key.h"
uint8_t KeyNum;
int8_t Speed;
int main(void)
{
	OLED_Init();
	Motor_Init();
	Key_Init();
	OLED_ShowString(1,1,"Speed:");
	while(1)
	{
	KeyNum=Key_GetNum();
	if(KeyNum == 1)
	{
	Speed+=20;
		if(Speed>100)
		{
		Speed=-100;
		}
	}
	Motor_SetSpeed(Speed);
	OLED_ShowSignedNum(1,7,Speed,3);
	}
}

6.7 TIM输入捕获

IC(Input Capture)输入捕获

输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中(将当前CNT的值读出并写入到CCR中),可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数

每个高级定时器和通用定时器都拥有4个输入捕获通道

可配置为PWMI模式,同时测量频率和占空比

可配合主从触发模式,实现硬件全自动测量

频率测量

测频法:在闸门时间T内,对上升沿计次,得到N,则频率

f_x=N / T

测周法:两个上升沿内,以标准频率fc计次,得到N ,则频率

f_x=f_c / N

中界频率:测频法与测周法误差相等的频率点

f_m=√(f_c / T)

输入捕获的基本结构:

其中的CNT,就是测周法中用来计数计时的参数。

根据上图,配置输入捕获的步骤为:

1、RCC开启时钟,将GPIO和TIM的时钟都打开。

2、GPIO初始化,将GPIO配置为输入模式。

3、配置时基单元,让CNT计数器在内部时钟的驱动下自增运行。

4、配置输入捕获单元。

5、选择从模式的触发源。

6、选择触发后执行的操作。

7、调用TIM_Cmd函数,开启定时器。

输入捕获模式测频率代码:

PWM.c:

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	/*RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//只有选择复用推挽输出,引脚的控制权才能转交给片上外设
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	/*选择时基单元的时钟*/
	TIM_InternalClockConfig(TIM2);
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef Tim_InitStructure;
	Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
	Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
	Tim_InitStructure.TIM_Period=100-1;//选择周期-ARR值
	Tim_InitStructure.TIM_Prescaler=720-1;//预分频器-PSC值
	Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
	TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
	/*配置输出比较单元*/
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//给结构体成员赋初始值
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出状态
	TIM_OCInitStructure.TIM_Pulse=0;//设置CCR寄存器的值
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);
	/*启动定时器*/
	TIM_Cmd(TIM2,ENABLE);
}
/*封装设定CCR值函数*/
void PWM_SetCompare1(uint16_t Compare)
{
	 TIM_SetCompare1(TIM2,Compare);
}
/*封装设定PSC值函数*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Update);
}

IC.c:

#include "stm32f10x.h"                  // Device header

void IC_Init(void)
{
	/*RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//只有选择复用推挽输出,引脚的控制权才能转交给片上外设
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	/*选择时基单元的时钟*/
	TIM_InternalClockConfig(TIM4);
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef Tim_InitStructure;
	Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
	Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
	Tim_InitStructure.TIM_Period=65536-1;//选择周期-ARR值
	Tim_InitStructure.TIM_Prescaler=72-1;//预分频器-PSC值
	Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
	TIM_TimeBaseInit(TIM4,&Tim_InitStructure);	
	/*初始化输入捕获单元*/
	TIM_ICInitTypeDef TIM_InitStructure;
	TIM_InitStructure.TIM_Channel=TIM_Channel_1;//选择通道
	TIM_InitStructure.TIM_ICFilter=1111;//配置输入捕获滤波器
	TIM_InitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//配置极性
	TIM_InitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//配置分频器
	TIM_InitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
	TIM_ICInit(TIM4,&TIM_InitStructure);
	/*配置TRGI的触发源为TI1FP1*/
	TIM_SelectInputTrigger(TIM4,TIM_TS_TI1FP1);
	/*配置从模式为Reset*/
	TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_Reset);
	/*调用TIM_Cmd函数*/
	TIM_Cmd(TIM4,ENABLE);
}

/*读取频率函数*/
uint32_t IC_GetFreq(void)
{
	return 1000000/(TIM_GetCapture1(TIM4)+1);
}

main函数:

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Delay.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{
	OLED_Init();
	PWM_Init();
	IC_Init();
	OLED_ShowString(1,1,"Freq:00000Hz");
	PWM_SetPrescaler(720-1);	//Freq = 72M / (PSC+1) / (ARR+1)
	PWM_SetCompare1(50);		//Duty = CCR /100
	while(1)
	{
	OLED_ShowNum(1,6,IC_GetFreq(),5);
	}
}

PWMI的基本结构:

与上图相比,多了一个分频器以及另一个捕获/比较器。具体的工作原理是:当出现上升沿电平时,CCR1开始计数,同时CNT清零。而加入了CCR2后,当电平由高转低时,CCR2捕获CNT的值,此时,CCR2的值就是高电平时CNT的值,而当到达下一个周期时,CCR1捕获CNT的值,那么CCR1的值就是一个周期内的值,那么CCR2/CCR1就是占空比。

根据上图,配置PWMI的步骤为:

1、RCC开启时钟,将GPIO和TIM的时钟都打开。

2、GPIO初始化,将GPIO配置为输入模式。

3、配置时基单元,让CNT计数器在内部时钟的驱动下自增运行。

4、配置输入捕获初始化为两个通道同时捕获同一引脚模式

PWMI模式测频率占空比:

IC.c:

#include "stm32f10x.h"                  // Device header

void IC_Init(void)
{
	/*RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//只有选择复用推挽输出,引脚的控制权才能转交给片上外设
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	/*选择时基单元的时钟*/
	TIM_InternalClockConfig(TIM4);
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef Tim_InitStructure;
	Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
	Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
	Tim_InitStructure.TIM_Period=65536-1;//选择周期-ARR值
	Tim_InitStructure.TIM_Prescaler=72-1;//预分频器-PSC值
	Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
	TIM_TimeBaseInit(TIM4,&Tim_InitStructure);	
	/*初始化输入捕获单元*/
	TIM_ICInitTypeDef TIM_InitStructure;
	TIM_InitStructure.TIM_Channel=TIM_Channel_1;//选择通道
	TIM_InitStructure.TIM_ICFilter=1111;//配置输入捕获滤波器
	TIM_InitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//配置极性
	TIM_InitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//配置分频器
	TIM_InitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
	TIM_PWMIConfig(TIM4,&TIM_InitStructure);//自动将剩下的通道初始化为相反的配置
	
	/*配置TRGI的触发源为TI1FP1*/
	TIM_SelectInputTrigger(TIM4,TIM_TS_TI1FP1);
	/*配置从模式为Reset*/
	TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_Reset);
	/*调用TIM_Cmd函数*/
	TIM_Cmd(TIM4,ENABLE);
}

/*读取频率函数*/
uint32_t IC_GetFreq(void)
{
	return 1000000/(TIM_GetCapture1(TIM4)+1);
}

/*获取占空比函数*/
uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM4)+1) * 100 / (TIM_GetCapture1(TIM4)+1);
}

PWM.c:

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	/*RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//只有选择复用推挽输出,引脚的控制权才能转交给片上外设
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	/*选择时基单元的时钟*/
	TIM_InternalClockConfig(TIM2);
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef Tim_InitStructure;
	Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
	Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
	Tim_InitStructure.TIM_Period=100-1;//选择周期-ARR值
	Tim_InitStructure.TIM_Prescaler=720-1;//预分频器-PSC值
	Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
	TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
	/*配置输出比较单元*/
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//给结构体成员赋初始值
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出状态
	TIM_OCInitStructure.TIM_Pulse=0;//设置CCR寄存器的值
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);
	/*启动定时器*/
	TIM_Cmd(TIM2,ENABLE);
}
/*封装设定CCR值函数*/
void PWM_SetCompare1(uint16_t Compare)
{
	 TIM_SetCompare1(TIM2,Compare);
}
/*封装设定PSC值函数*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Update);
}

main函数:

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	/*RCC开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	/*配置GPIO*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//只有选择复用推挽输出,引脚的控制权才能转交给片上外设
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	/*选择时基单元的时钟*/
	TIM_InternalClockConfig(TIM2);
	/*配置时基单元*/
	TIM_TimeBaseInitTypeDef Tim_InitStructure;
	Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
	Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
	Tim_InitStructure.TIM_Period=100-1;//选择周期-ARR值
	Tim_InitStructure.TIM_Prescaler=720-1;//预分频器-PSC值
	Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
	TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
	/*配置输出比较单元*/
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);//给结构体成员赋初始值
	TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//设置输出比较模式
	TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性
	TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出状态
	TIM_OCInitStructure.TIM_Pulse=0;//设置CCR寄存器的值
	TIM_OC1Init(TIM2,&TIM_OCInitStructure);
	/*启动定时器*/
	TIM_Cmd(TIM2,ENABLE);
}
/*封装设定CCR值函数*/
void PWM_SetCompare1(uint16_t Compare)
{
	 TIM_SetCompare1(TIM2,Compare);
}
/*封装设定PSC值函数*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Update);
}

6.8 TIM编码器接口

Encoder Interface 编码器接口

编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度

每个高级定时器和通用定时器都拥有1个编码器接口

两个输入引脚借用了输入捕获的通道1和通道2

编码器接口的基本结构:

由上图,编码器接口初始化可分为以下几步:

1、RCC开启时钟。

2、配置GPIO。

3.配置时基单元。

4、配置输入捕获单元。

5、配置编码器接口模式。

6.调用TIM_Cmd函数启动定时器。

 编码器接口的工作模式:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了STM32F1系列芯片的型号分类,根据Flash的大小可以分为小容量产品、中容量产品和大容量产品。其中,小容量产品的Flash大小为16~32K,简写为LD(Low Density);中容量产品的Flash大小为64~128K,简写为MD(Medium Density);大容量产品的Flash大小为256~512K,简写为HD(High Density)。\[1\] 引用\[2\]中提到了在STM32中,可以通过读写“位带”区域来操作某一位的数据。这段地址区域映射了RAM和外设寄存器的所有位。可以使用函数来操作位设置和位清除寄存器的方法。在GPIO模式中,可以通过配置GPIO的端口配置寄存器将端口配置为8种不同的模式。这些模式的电路结构基本相同,区别在于上拉电阻和下拉电阻的连接。这些模式都属于数字输入口,可以读取端口的高低电平。在使用浮空输入时,端口必须接上一个连续的驱动源,不能出现悬空的状态。还有一种模式是模拟输入。\[2\] 引用\[3\]中提到了对基本定时器的定时进行了简单介绍,并给出了相应的源码和注释,方便新手学习参考。在使用定时器之前,需要使能定时器时钟和设置TIM的时钟频率。可以根据时钟树进行配置,也可以直接配置TIM的时钟频率。\[3\] 关于STM32F103标准tim.c的具体内容,由于没有提供相关引用内容,无法给出具体答案。请提供更多相关信息,以便我能够给出更准确的回答。 #### 引用[.reference_title] - *1* *2* [STM32F103标准入门——新建工程、初识GPIO](https://blog.csdn.net/WandZ123/article/details/124531339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [STM32F103标准 TIM定时详细例程](https://blog.csdn.net/qq_43476908/article/details/127814797)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值