6、PWM驱动

一、输出比较(输出PWM波形)

在通用计时器图中,CNT是时基单元的计数器,CCR是捕获/比较寄存器

在输出比较这里,这块电路就会比较CNT和CCR的值,CNT计数自增,CCR是我们给定的一个值,当CNT大于CCR或者小于CCR时,这里(TIMx)的输出就会对应的置1、置0,置1、置0,这样子就可以输出一个电平不断变化的PWM波形了。

二、PWM(天下武功,唯快不破)

PWM:脉冲宽度调制。

这个PWM波形是一个数字输出信号,也是有高低电平组成的,像上图右下角的这样一个连续变化电平信号。这个信号有什么用呢?在具有惯性的系统中,通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常用于电机控速。也就是说,使用这个PWM波形,是用来等效地实现一个模拟信号地输出。我们以一个很快的频率给电机通电、断电、通电、断电,那么电机的速度就能维持在一个中等速度了。当我们调控这个通电断电的时间比例时,就能让电机呈现出不同的速度级别。

注意的是:应用场景必须是惯性系统,LED在熄灭时,由于余辉和人眼视觉暂留现象,LED不会立马熄灭,而是有一定的惯性,过一段时间才会熄灭;电机也是,当电机断电时,电机的转动不会立马停止,而是有一定的惯性,过一会才停,这样具有惯性的系统才能使用PWM。

左下角这种高低电平跳变的数字信号,是可以等效为中间这个虚线(曲线)所表示的模拟量的,当上面的电平时间长一点,下面电平时间短一点的时候,那等效的模拟量就偏向于上面,当下面电平时间长一点,上面电平时间短一点的时候,等效的模拟量就等效于下面。

在使用PWM时,以下这几个参数比较重要:

1、频率=1/Ts,Ts=Ton+Toff(代表一个高低电平变换周期的时间),频率越快,Ts越小,即等效模拟的信号就越平稳,不过同时性能开销就越大,一般来说频率在几千到几十千就已经足够快了。

2、占空比=Ton/Ts,(高电平时间相当于整个周期时间的比例,百分比表示)它决定了PWM等效出来的模拟电压的大小,占空比越大,那等效的模拟电压就越趋近于高电平,反之则越接近低电平,这个等效关系一般来说是线性的,比如高电平是5V,低电平是0V,那50%占空比就等效于中间电压,就是2.5V;20%占空比就等效于五分之一处(从下往上五分之一的位置)的电压,就是1V。

3、分辨率=占空比变化步距,比如有的占空比只能是1%、2%、3%等等这样以1%的步距跳变,那他的分辨率就是1%;如果可以1.1%、1.2%、1.3%等等这样以0.1%的步距跳变,那他的分辨率就是0.1% 。所以这个分辨率就是占空比变化的精细程度,这个分辨率需要多高,取决于项目的需求。

三、输出比较通道

高级定时器前的前三道的输出比较部分电路原理图(备用):

下面了解定时器的输出比较模块是怎样来输出PWM波形的

CNT与CCR1比较,当CNT>CCR1或者CNT=CCR1时,就会给这个输出模式控制器传一个信号,然后输出模式控制器就会改变它的输出OC1REF的高低电平,REF信号(reference参考信号)实际上就是指这里信号的高低电平;(上面的ETRF输入,这是定时器的一个小功能,一般不用);接着,一方面REF信号可以前往主模式控制器,可以把这个REF映射到主模式的TRGO输出上去,

另一方面是下面这一路,也是主要的去向,通过下路到达一个极性选择区,由图,给这个信号写0,信号就会往上走,就是信号电平不用翻转的意思,给这个信号写1,信号就会往下走,信号通过一个非门取反,输出的信号就是输入信号高低电平反转的信号。这就是极性选择,就是选择是不是要把高低电平反转一下。

接着就到了输出使能电路了,选择需不需要输出。最后就是OC1引脚,这个引脚就是CH1通道的引脚,这个就是这个部分CH1通道的引脚,在引脚定义表里可以查到具体是哪个GPIO口了。

接着还需要看一下这个输出模式控制器的工作方式,(具体怎么工作的,什么时候给REF高电平,什么时候给REF低电平):

四、输出比较模式

 上图为八个输出比较模式,即这个输出模式控制器里面的执行逻辑。这个模式控制器的输入是CNT和CCR的大小关系,输出的是REF的高低电平,里面可以选择多种模式来更加灵活地控制REF输出。中间的这个模式就可以通过寄存器来进行配置,需要哪个模式就选择哪个模式。

 (1)冻结:可以理解为CNT和CCR无效,REF保持原来状态;不管谁大谁小,直接REF保持不变,维持上一个状态就行了。例子,比如我们正在输出PWM波,突然想暂停一会输出,就可以设置成这个模式,一旦切换为冻结模式后,输出就暂停了,并且高低电平也维持为暂停时刻的状态,保持不变。

(2)(3)(4)这个有效电平属于一些高级定时器的说法,是和关断、刹车这些功能配合表述的,说的比较严谨。可以直接认为置有效电平就是置高电平,置无效电平就是置低电平。这些模式就可以用做波形输出了。(2)和(3)用途不大,属于一次性的模式,不适合输出连续变化的波形,想定时输出一个一次性的信号就可以用到;第(4)个,电平翻转这个模式,可以方便地输出一个频率可调、占空比始终为50%的PWM波形。例如,你设置CCR为0,那CNT每次更新清零时就会产生一次CNT=CCR的事件这就会导致输出电平翻转一次,每更新两次,输出为1个周期,并且高电平和低电平的时间是始终相等的,也就是占空比始终为50%,如下图: 

当改变定时器更新频率时,输出波形的频率也会随之改变,他俩的关系是输出波形的频率=更新频率/2,因为更新两次输出才为一个周期,这就是这个匹配时电平翻转模式的用途。

(5)和(6),和冻结模式差不多,想在暂停期间保持低电平或者高电平,就可以用这两个。

(7)和(8)这两个模式非常重要,他们可以用来输出频率和占空比都可调的PWM波形,也是我们主要使用的模式。(1上小高) PWM模式2实际上就是PWM模式1输出的取反,改变PWM模式和PWM模式2,就只是改变REF电平的极性而已。通路后面有一个极性部分,所以使用PWM模式1的正极性和PWM模式2的反极性最终的输出是一样的。这样子意味着输出后可以设置极性,输出前也可以。

重点:解释(7)“1上小高”模式是怎样输出频率和占空比都可调的PWM波形:

CNT不断自增运行,同时它俩还在不断进行比较。

如图,蓝色线CNT,黄色线ARR,CTT从0开始自增到99后清零,设置一条红线即CCR,设置CCR为30,再执行这个逻辑,下面绿色线就是输出,可以看到在第一块,红线高于蓝线,即CNT<CCR,所以置高电平,之后CNT>=CCR,所以就变成低电平。可以看出占空比是受CCR值调控的。所以REF就是一个频率可调,占空比也可调的PWM波形。

五、PWM参数计算

接下来是PWM的参数计算:

 舵机电机:

 50HZ,角度y=90t-135

 

六、重映射

在PWM.c中,设置了在TIM2的OC1通道上就可以输出PWM波形了,但最终这个波形肯定是要借用一下GPIO口才能输出。但是这个GPIO口通道是借助了哪一个GPIO口呢?打开引脚定义表:默认复用功能就是片上外设的端口与GPIO的连接关系。在第十行这里可以看到,有TIM2_CH1_ETR,他是在这个PA0行的,这就说明,TIM2的ETR引脚和通道1的引脚,都是借用了PA0这个引脚的位置的,换句话说就是TIM2的引脚复用在了PA0引脚上,所以说如果我们要使用TIM2的OC1也就是CH1通道,输出PWM,那他就只能在PA0的引脚引出,而不是任意选择引脚输出;同样如果使用TIM2的CH2,那就只能在PA1端口输出,TIM2的CH3,就只能是PA2,CH4就只能是PA3;其他外设也是同理,比如我们要使用SPI的MISO引脚,那就是PA6,这个关系是定死的,不能随意更改。不过虽然它是定死的,STM32还是给了一次更改的机会,这就是重定义(重映射),比如如果你既要要USART的TX引脚,又要用TIM2的CH3通道,它俩冲突了没办法同时用,那我们就可以在这个重映射的列表里找一下,比如我们这里找到了TIM2的CH3,那么TIM2的CH3就可以从原来的引脚,换到这里的引脚,这样就避免了两个外设引脚的冲突,如果这个重映射的列表找不到,那外设复用的GPIO就不能挪位置,这就是重映射的功能,配置重映射是用AFIO来完成的。

(p15上电后已经默认复用为了调试端口JTDI,所以想让它作为普通的GPIO或者复用定时器的通道,那还需要先关闭调试端口的复用,用GPIO_PinRemapConfig()函数来关闭,跳到它的参数,

这里有三个参数用来解除调试窗口的复用的。SWJ就是SWD和JTAG这两种调试方式,第一个NoJTRST,就是解除NJTRST引脚的复用,回到引脚定义里看,其对应的是PB4,如果使用这个参数,那么这个PB4就变成正常的GPIO口了,其他的四个端口(PA13、PA14、PA15、PB3)仍然为调试端口,不能当作GPIO来使用。第二个参数JTAGDisable,这个就是解除JTAG调试端口的复用,在引脚定义里就是,PA15、PB3、PB4这三个端口变回GPIO,上面的PA13和PA14仍然为SWD的调试端口。第三个参数SWJ_Disable,这个参数就是把SWD和JTAG的调试端口全部解除,在引脚定义里就是这五个引脚(PA13\14\15,PB3\4)全部变成普通的GPIO,没有调试功能了,所以这个参数慎用,一旦调用这个参数并且下载程序后,那么你的调试端口都没有了,这以后在使用STLINK就下载不进去程序了。这时只能使用串口下载,下载一个新的、没有解除调试端口的程序,这样才能把调试端口弄回来。

    //如果要使用P15端口当作普通GPIO口使用,那么要先打开AFIO时钟
    //再用AFIO将JTAG复用解除掉,这样就可以了
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
    //如果你想重映定时器或者其他外设的复用引脚,那就先打开AFIO时钟,
    //再用AFIO重映射外设复用的引脚,就可以了

七、三个驱动代码

1)PWM驱动呼吸灯

PWM.c
#include "stm32f10x.h"                  // Device header
//PWM初始化
void PWM_Init()
{
	//第一步,RCC开启时钟,把TIM外设和GPIO外设的时钟打开
	//第二步,配置时基单元,(时钟源)
	//第三步,配置输出比较单元(CCR值、输出比较模式、极性选择、输出使能)
	//第四步,配置GPIO,把对应的GPIO口,初始化为复用推挽输出配置
	//第五步,启动计数器(运行控制),这样子就可以输出PWM了
	
	
	//第一步,RCC开启时钟,这个基本上每个代码都是第一步,打开时钟后,定时器的基准时钟和整个外设的工作时钟就都会同时打开了
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	//如果要使用P15端口当作普通GPIO口使用,那么要先打开AFIO时钟
//	//再用AFIO将JTAG复用解除掉,这样就可以了
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
//	//如果你想重映定时器或者其他外设的复用引脚,那就先打开AFIO时钟,
//	//再用AFIO重映射外设复用的引脚,就可以了
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	
	//第二步,选择时基单元的时钟源,对于定时中断,我们选择内部时钟源
	TIM_InternalClockConfig(TIM2);
	
	//第三步,配置时基单元,用一个结构体来配置这里的预分频器、自动重装器、计数器(模式)等等
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=100-1;//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	//接下来赋初始值,用(TIM_OC)StructInit函数给结构体赋初始值
	TIM_OCInitTypeDef TIM_OCinitStruture;
	TIM_OCStructInit(&TIM_OCinitStruture);
	TIM_OCinitStruture.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式
	TIM_OCinitStruture.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较的极性
	TIM_OCinitStruture.TIM_OutputState=TIM_OutputState_Enable;//设置输出使能
	TIM_OCinitStruture.TIM_Pulse=0;//设置CCR
	TIM_OC1Init(TIM2,&TIM_OCinitStruture);
	
//	//第四步,配置输出中断控制,允许更新中断输出到NVIC
//	
//	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//	
//	//第四步,配置输出中断控制,允许更新中断输出到NVIC
//	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//	
//	//第五步,配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//	NVIC_InitTypeDef NVIC_InitStucture;
//	NVIC_InitStucture.NVIC_IRQChannel=TIM2_IRQn;
//	NVIC_InitStucture.NVIC_IRQChannelCmd=ENABLE;
//	NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority=2;
//	NVIC_InitStucture.NVIC_IRQChannelSubPriority=1;
//	NVIC_Init(&NVIC_InitStucture);
	//第六步,运行控制(使能计数器,不然其不运行)
	TIM_Cmd(TIM2,ENABLE);
	
}
//在运行过程中改变CRR,用这个函数可以单独更改通道1的CCR值
void PWM_SetComparel(uint16_t Compare){
	TIM_SetCompare1(TIM2,Compare);
}
main.c
#include "stm32f10x.h"
#include "OLED.h"
#include "Delay.h"
#include "PWM.h"
uint8_t i;
int main(){
	OLED_Init();
	PWM_Init();
	while(1){
		//占空比从0到100,LED变亮
		for(i=0;i<=100;i++){
			PWM_SetComparel(i);
			Delay_ms(10);
		}
		//占空比从100到0,LED变暗
		for(i=0;i<=100;i++){
			PWM_SetComparel(100-i);
			Delay_ms(10);
		}
	}
}

2)PWM驱动舵机

PWM.c
#include "stm32f10x.h"                  // Device header
//PWM初始化
void PWM_Init()
{
	//第一步,RCC开启时钟,把TIM外设和GPIO外设的时钟打开
	//第二步,配置时基单元,(时钟源)
	//第三步,配置输出比较单元(CCR值、输出比较模式、极性选择、输出使能)
	//第四步,配置GPIO,把对应的GPIO口,初始化为复用推挽输出配置
	//第五步,启动计数器(运行控制),这样子就可以输出PWM了
	
	
	//第一步,RCC开启时钟,这个基本上每个代码都是第一步,打开时钟后,定时器的基准时钟和整个外设的工作时钟就都会同时打开了
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	
	//第二步,选择时基单元的时钟源,对于定时中断,我们选择内部时钟源
	TIM_InternalClockConfig(TIM2);
	
	//第三步,配置时基单元,用一个结构体来配置这里的预分频器、自动重装器、计数器(模式)等等
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=2000-1;//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	//接下来赋初始值,用(TIM_OC)StructInit函数给结构体赋初始值
	TIM_OCInitTypeDef TIM_OCinitStruture;
	TIM_OCStructInit(&TIM_OCinitStruture);
	TIM_OCinitStruture.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式
	TIM_OCinitStruture.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较的极性
	TIM_OCinitStruture.TIM_OutputState=TIM_OutputState_Enable;//设置输出使能
	TIM_OCinitStruture.TIM_Pulse=0;//设置CCR
	TIM_OC2Init(TIM2,&TIM_OCinitStruture);
	
//	//第四步,配置输出中断控制,允许更新中断输出到NVIC
//	
//	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//	
//	//第四步,配置输出中断控制,允许更新中断输出到NVIC
//	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//	
//	//第五步,配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//	NVIC_InitTypeDef NVIC_InitStucture;
//	NVIC_InitStucture.NVIC_IRQChannel=TIM2_IRQn;
//	NVIC_InitStucture.NVIC_IRQChannelCmd=ENABLE;
//	NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority=2;
//	NVIC_InitStucture.NVIC_IRQChannelSubPriority=1;
//	NVIC_Init(&NVIC_InitStucture);
	//第六步,运行控制(使能计数器,不然其不运行)
	TIM_Cmd(TIM2,ENABLE);
	
}
//在运行过程中改变CRR,用这个函数可以单独更改通道1的CCR值
void PWM_SetCompare2(uint16_t Compare){
	TIM_SetCompare2(TIM2,Compare);
	
}
Servo.c
#include "stm32f10x.h"                  // Device header
#include "PWM.h"//继承PWM的功能
//舵机底层初始化
void Servo_Init(void)
{
	PWM_Init();
}
//舵机设置角度,在里面调用PWM_SetCompare2函数,里面参数关系进去
//0度对应CCR500
//180度对应CCR2500
void Servo_SetAngle(float Angle)
{
	PWM_SetCompare2(Angle/180*2000+500);//0度对的是500,所以要加偏移
}
main.c
#include "stm32f10x.h"
#include "OLED.h"
#include "Delay.h"
#include "PWM.h"
#include "Servo.h"
#include "key.h"
uint8_t KeyNum;//按键键码
float Angle;//角度变量
int main(){
	OLED_Init();
	PWM_Init();
	Key_Init();
	//PWM_SetCompare2(500);//对应90度的位置
	//PWM_SetCompare2(2500);//对应180度的位置
	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);
	}
}

3)PWM驱动直流电机

Motor.c
#include "stm32f10x.h"                  // Device header
#include "PWM.h"
void Motor_Init(){
	PWM_Init();
	//这里要额外初始化方向控制的两个脚

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能时钟,开启外设时钟
                                                        //(选择外设,选择新的状态)
	GPIO_InitTypeDef GPIO_InitStructure;//定义结构体
	GPIO_InitStructure.GPIO_Mode=  GPIO_Mode_Out_PP;//GPIO模式=通用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;//GPIO端口=(跳转定义时选member)
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//GPIO速度
	GPIO_Init(GPIOA,&GPIO_InitStructure);//配置端口模式(选择GPIO,选择参数的结构体)
	//GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5);//指定端口设置为高电平,如果不操作则暗
}

//下面是速度
void Motor_SetSpeed(int8_t Speed){
	//针对正转和反转用if判断处理
	if(Speed>=0){
		//方向
		GPIO_SetBits(GPIOA,GPIO_Pin_4);
		GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		//速度
		PWM_SetCompare3(Speed);	
	}
	else
	{
		//方向
		GPIO_ResetBits(GPIOA,GPIO_Pin_4);
		GPIO_SetBits(GPIOA,GPIO_Pin_5);
		//速度
		PWM_SetCompare3(-Speed);//这时Speed为负数,但是SetCompare必须传正数,所以Speed前面要加一个负号
		
	
	}
}
PWM.c
#include "stm32f10x.h"                  // Device header
//PWM初始化
void PWM_Init()
{
	//第一步,RCC开启时钟,把TIM外设和GPIO外设的时钟打开
	//第二步,配置时基单元,(时钟源)
	//第三步,配置输出比较单元(CCR值、输出比较模式、极性选择、输出使能)
	//第四步,配置GPIO,把对应的GPIO口,初始化为复用推挽输出配置
	//第五步,启动计数器(运行控制),这样子就可以输出PWM了
	
	
	//第一步,RCC开启时钟,这个基本上每个代码都是第一步,打开时钟后,定时器的基准时钟和整个外设的工作时钟就都会同时打开了
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	//如果要使用P15端口当作普通GPIO口使用,那么要先打开AFIO时钟
//	//再用AFIO将JTAG复用解除掉,这样就可以了
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
//	//如果你想重映定时器或者其他外设的复用引脚,那就先打开AFIO时钟,
//	//再用AFIO重映射外设复用的引脚,就可以了
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	
	//第二步,选择时基单元的时钟源,对于定时中断,我们选择内部时钟源
	TIM_InternalClockConfig(TIM2);
	
	//第三步,配置时基单元,用一个结构体来配置这里的预分频器、自动重装器、计数器(模式)等等
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period=100-1;//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler=36-1;//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
	
	//接下来赋初始值,用(TIM_OC)StructInit函数给结构体赋初始值
	TIM_OCInitTypeDef TIM_OCinitStruture;
	TIM_OCStructInit(&TIM_OCinitStruture);
	TIM_OCinitStruture.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式
	TIM_OCinitStruture.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较的极性
	TIM_OCinitStruture.TIM_OutputState=TIM_OutputState_Enable;//设置输出使能
	TIM_OCinitStruture.TIM_Pulse=0;//设置CCR
	TIM_OC3Init(TIM2,&TIM_OCinitStruture);
	
//	//第四步,配置输出中断控制,允许更新中断输出到NVIC
//	
//	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//	
//	//第四步,配置输出中断控制,允许更新中断输出到NVIC
//	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//	
//	//第五步,配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//	NVIC_InitTypeDef NVIC_InitStucture;
//	NVIC_InitStucture.NVIC_IRQChannel=TIM2_IRQn;
//	NVIC_InitStucture.NVIC_IRQChannelCmd=ENABLE;
//	NVIC_InitStucture.NVIC_IRQChannelPreemptionPriority=2;
//	NVIC_InitStucture.NVIC_IRQChannelSubPriority=1;
//	NVIC_Init(&NVIC_InitStucture);
	//第六步,运行控制(使能计数器,不然其不运行)
	TIM_Cmd(TIM2,ENABLE);
	
}
//在运行过程中改变CRR,用这个函数可以单独更改通道1的CCR值
void PWM_SetCompare3(uint16_t Compare){
	TIM_SetCompare3(TIM2,Compare);
}
main.c
#include "stm32f10x.h"
#include "OLED.h"
#include "Delay.h"
#include "PWM.h"
#include "key.h"
#include "Motor.h"
int8_t speed;
uint8_t KeyNum;
int main(){
	OLED_Init();
	PWM_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);
	}
}

OVER

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值