江科大STM32视频学习笔记:PWM驱动LED呼吸灯、舵机和直流电机

目录

一、PWM驱动LED呼吸灯(灯接在PA0)

1、PWM波和GPIO的对应关系参考引脚定义表

2、计数器的计算

3、TIM输出PWM波使用步骤​编辑

4、代码

(1)输出化比较单元

(2) PWM.c

(3)main.c

5、重映射更换成PA15亮灯

二、PWM驱动舵机(舵机接在PA1、按键在PB1) 

1、电路图

2、参数计算

3、代码

(1) PWM.c修改的地方

(2)PWM.c完整代码

(3)Servo.c

(4)main.c

三、PWM驱动直流电机

1、原理图

2、代码

(1)PWM.c中改的地方

(2)Motor.c

(3)main.c

四、基础知识


一、PWM驱动LED呼吸灯(灯接在PA0)

1、PWM波和GPIO的对应关系参考引脚定义表

(1)TIM2的引脚复用在了PA0引脚上,故要是要TIM2的OC1也就是CH1通道,输出PWM,那它只能在PA0的引脚上输出,而不能任意选择引脚输出

(2)同样,若选择 TIM2的是CH2通道,那只能在PA1端口输出,以此类推

(3)但还是有一次重映射的机会,可以在其他引脚上输出 

2、计数器的计算

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

若要产生一个频率为1kHz,占空比为50%,分辨率为1%的PWM波形

带入公式:72M/(PSC+1)/(ARR+1)=1000

                  CCR/(ARR+1) = 50%

                  1/(ARR+1)=1%

则计算出:ARR+1=100,CCR=50,PSC+1=720 

则等以1kHZ的频率闪烁,但是我们看不出来

    TIM_TimeBaseInitStructure.TIM_Period = 100 – 1;        //ARR
    TIM_TimeBaseInitStructure.TIM_Prescaler = 720 – 1;        //PSC
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;

3、TIM输出PWM波使用步骤

第一步:RCC开启时钟,打开TIM外设和GPIO外设的时钟

第二步:配置时基单元,包括时基单元和时基单元前的时钟源选择

第三步:配置输出比较单元,里面包括CCR的值、输出比较模式、极性选择、输出使能(结构体配置)

第四步:配置GPIO口,初始化为复用推挽输出的配置

第五步:运行控制,启动计数器,就能输出PWM波

4、代码

(1)输出化比较单元

	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的值0-ffff
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);//用结构体初始化	

(2) PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	//对于普通的开漏推挽输出,引脚的控制权是来自于输出数据寄存器,
	//若想要定时器控制引脚,需要使用复用开漏/推挽输出的模式
	//此时,输出数据寄存器将被断开,输出控制权将转移到片上外设
	//本实验中片上外设引脚连接的就是TIM2的CH1通道,所以只有把GPIO设置程复用推挽输出
	//引脚的控制权才能交给片上外设,PWM波形才能通过引脚输出
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	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);
	
	//初始化输出比较单元
	//PA0口对应1通道
	TIM_OCInitTypeDef TIM_OCInitStructure;
	//不想把所有成员都列出来赋一个初始值,就用TIM_OCStructInit初始化值,防止出现奇怪的错误
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM模式1和PWM模式2
	//输出比较极性:
	//TIM_OCPolarity_High高级性,就是极性不翻转,REF波形直接输出,或者说有效电平是高电平
	//TIM_OCPolarity_Low低极性,就是REF波形电平取反,有效电平为低电平
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;		//设置CCR的值0-ffff
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);//用结构体初始化	
	
	TIM_Cmd(TIM2, ENABLE);//启动定时器
}

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);//设置占空比
}

(3)main.c

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

uint8_t i;

int main(void)
{
	OLED_Init();
	PWM_Init();
	
	while (1)
	{
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(i);//更改占空比的函数
			Delay_ms(10);
		}
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(100 - i);
			Delay_ms(10);
		}
	}
}

5、重映射更换成PA15亮灯

 想要把PA0改成PA15,就可以选重映射方式1或者完全重映射

在"GPIO_InitTypeDef GPIO_InitStructure;"前面添加

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);              (1)

GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);                    (2)
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE)           (3)

PA15是调试端口,所以要解除调试端口 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE)

  • 若想要让PA15、PB3、PB4这三个引脚当作普通GPIO来使用,则加第一、三句,打开AFIO时钟,让AFIO时钟将JTAG复用解除掉
  • 如果想要重映射定时器或者其他外设的复用引脚,加第一句和第二句,就要先打开AFIO时钟,再用AFIO重映射外设复用的引脚,
  • 若重映射的引脚正好是调试端口,则三句全加上
  • 改变引脚:

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;

    改为

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;

    此时PA0的灯不亮,而PA15的灯亮了

    二、PWM驱动舵机(舵机接在PA1、按键在PB1) 

    1、电路图

    2、参数计算

  • 舵机要求的频率:20ms,故为50Hz
  • 占空比:舵机要求高电平时间是0.5ms~2.5ms
  • 计算出来的PSC和ARR不固定
  • 测试PSC+1=72,ARR+1=20k时比较方便,此时,CCR=500,则0.5ms,CCR=2500,则2.5ms
  • CCR=500,0度,CRR=2500,180度
  • 3、代码

    (1) PWM.c修改的地方

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;		//ARR
    TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;		//PSC
    
    void PWM_SetCompare2(uint16_t Compare)//从通道1改为使用通道2
    {
    	TIM_SetCompare2(TIM2, Compare);
    }

    (2)PWM.c完整代码

    #include "stm32f10x.h"                  // Device header
    
    void PWM_Init(void)
    {
    	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_Pin = GPIO_Pin_1;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	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 = 20000 - 1;		//ARR
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;		//PSC
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    	
    	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);
    }
    
    void PWM_SetCompare2(uint16_t Compare)//使用通道2
    {
    	TIM_SetCompare2(TIM2, Compare);
    }

    (3)Servo.c

    #include "stm32f10x.h"                  // Device header
    #include "PWM.h"
    
    void Servo_Init(void)
    {
    	PWM_Init();
    }
    
    void Servo_SetAngle(float Angle)
    {
    	PWM_SetCompare2(Angle / 180 * 2000 + 500);
    }
    

    (4)main.c

    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.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);
    	}
    }

    三、PWM驱动直流电机

    1、原理图

  •  红色的是TB6612电机驱动模块
  • 连接电机的两根线不分正反,对调知识反过来转动,AIN1和AIN2是方向控制,任意连接两个GPIO就行(此处接PA4、PA5),PWMA是速度控制,需要接PWM的输出脚(PA2,PA2对应的是TIM2的通道3,到时候初始化通道三即可)
  • 2、代码

    (1)PWM.c中改的地方

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//(0改为2)
    TIM_OC3Init(TIM2, &TIM_OCInitStructure);//(1改为3)
    
    
    TIM_TimeBaseInitStructure.TIM_Period = 1000 - 1;		//ARR
    TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;		//PSC
    //改频率,将1kHZ改为20kHz
    TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARR
    TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1;		//PSC
    
    void PWM_SetCompare3(uint16_t Compare)//(1改为3)
    {
    	TIM_SetCompare3(TIM2, Compare);
    }
    

    (2)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_SetCompare3(Speed);
    	}
    	else
    	{
    		GPIO_ResetBits(GPIOA, GPIO_Pin_4);//反转
    		GPIO_SetBits(GPIOA, GPIO_Pin_5);
    		PWM_SetCompare3(-Speed);
    	}
    }
    

    (3)main.c

    #include "stm32f10x.h"                  // Device header
    #include "Delay.h"
    #include "OLED.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);
    	}
    }
    

    四、基础知识

    void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState)

    仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数,使能主输出,否则PWM将不能正常输出

    物联沃分享整理
    物联沃-IOTWORD物联网 » 江科大STM32视频学习笔记:PWM驱动LED呼吸灯、舵机和直流电机

    发表评论