zl程序教程

您现在的位置是:首页 >  Javascript

当前栏目

6.4 PWM驱动LED呼吸灯&PWM驱动舵机&PWM驱动直流电机

2023-04-18 14:23:16 时间
  1. PWM驱动LED呼吸灯

1.1 电路连接示意图

PA0引脚上插一个LED,在PA0引脚输出一个PWM波,用于驱动LED呈现出不同亮度。

LED正极接在PA0引脚,负极接在GND(高电平电路,低电平熄灭),这样占空比越大,LED越亮,占空比越小,LED越暗。

1.2 设计思路

本设计采用模块化编程,在配置好的工程基础上在Hardware文件夹中加上PWM.c、PWM.h两部分。

灯的亮度与CCR的值有关,CCR值越大,灯越亮,CCR值越小,灯越暗

PWM初始化步骤

  1. RCC开启时钟,把要用的TIM外设和GPIO外设的时钟打开。

  1. 配置时基单元,配置好时钟选择和时基单元。

  1. 配置输出比较单元,包含CCR的值、输出比较模式、极性选择、输出使能。

  1. 配置GPIO,把PWM对应的GPIO口,初始化为复用推挽输出的配置。

  1. 运行控制。

1.3 知识储备--输出比较常用库函数

  • 配置输出比较函数

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_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
  • 配置强制输出模式函数

在运行中想要暂停输出波形并且强制输出高或者低电平,强制输出高电平和设置百分百占空比相同,强制输出低电平和设置百分百零低电平相同。

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);
  • 配置CCR寄存器的预装功能函数

预装功能就是影⼦寄存器:写⼊的值不会⽴即⽣效,⽽是在更新事件才会⽣效

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);
  • 单独设置输出⽐较的极性函数

带N的是高级定时器互补通道的配置函数,OC4没有互补通道,无OC4N函数

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);
  • 单独更改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);
  • 使用高级定时器输出PWM时调用使能主输出函数

void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);

1.4 程序设计

PWM.h

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);

#endif

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//开启TIM2时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIO时钟
    
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
//    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体
    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_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_OCInitTypeDef TIM_OCInitStructure;//定义输出比较结构体
    TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋默认值
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//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的值 CCR越大 灯越亮 CCR值越小 灯越暗
void PWM_SetCompare1(uint16_t Compare)
{
    TIM_SetCompare1(TIM2, Compare);//设置CCR3的值
}

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);//占空比从0--100 LED不断变亮
            Delay_ms(10);
        }
        for (i = 0; i <= 100; i++)
        {
            PWM_SetCompare1(100 - i);//占空比从100-0 LED逐渐变暗
            Delay_ms(10);
        }
    }
}

2. PWM驱动舵机

1.1 电路连接示意图

三根线:棕色线GND 接GND

红色线5V接ST-LINK 5V输出引脚。

橙色线PWM接PA1引脚

PB1接一个按键用来控制舵机

1.2 设计思路

驱动舵机的关键输出PWM波形,通过控制占空比来控制转动角度,详情请看上一节。

设置按键,每按一次,舵机转动30度。

本设计采用模块化编程,在配置好的工程基础上在Hardware文件夹中加上PWM.c、PWM.h、Servo.c、Servo.h四部分。

1.3 程序设计

PWM.h

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);

#endif

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//开启TIM2时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIO时钟
    
    GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体
    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  舵机要求的周期是20ms
    TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;        //PSC  舵机要求高电平时间0.5ms--2.5ms
    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;//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);
}
//使用通道2 单独更改CCR的值
void PWM_SetCompare2(uint16_t Compare)
{
    TIM_SetCompare2(TIM2, Compare);//设置CCR3的值
}

Servo.h

#ifndef __SERVO_H
#define __SERVO_H

void Servo_Init(void);
void Servo_SetAngle(float Angle);

#endif

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);
}

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;//按一次加30度
            if (Angle > 180)
            {
                Angle = 0;
            }
        }
        Servo_SetAngle(Angle);
        OLED_ShowNum(1, 7, Angle, 3);//显示角度值
    }
}

3. PWM驱动直流电机

3.1电路连接示意图

TB6612电机驱动模块

VM点电机电源--ST-LINK 5V引脚

VCC逻辑电源--面包板3.3V

GND电源负极--面包板负极

AO1 AO2 电机输出端--接电机两根线

STBY待机控制脚--接逻辑电源3.3V

A1N1 A1N2方向控制--接任意两个GPIO口

PWMA速度控制--PWM输出脚 PA2对应的是TIM2通道3

按键--PB1口用于控制

3.2 设计思路

本设计采用模块化编程,在配置好的工程基础上在Hardware文件夹中加上PWM.c、PWM.h、Motor.c、Motor.h四部分。

3.3 程序设计

PWM.h

#ifndef __PWM_H
#define __PWM_H

void PWM_Init(void);
void PWM_SetCompare3(uint16_t Compare);

#endif

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//开启TIM2时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIO时钟
    
    GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//开启引脚
    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);//写入参数
    
    TIM_OCInitTypeDef TIM_OCInitStructure;//定义输出比较结构体
    TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋默认值
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM1模式
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效电平为高电平
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//使能
    TIM_OCInitStructure.TIM_Pulse = 0;        //CCR
    TIM_OC3Init(TIM2, &TIM_OCInitStructure);//写入参数
    
    TIM_Cmd(TIM2, ENABLE);
}
//使用通道2 单独更改CCR的值
void PWM_SetCompare3(uint16_t Compare)
{
    TIM_SetCompare3(TIM2, Compare);//设置CCR3的值
}

Motor.c

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

void Motor_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIOA时钟
    
    GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体
    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);//初始化GPIOA
    
    PWM_Init();//初始化PWM
}
//设置速度-100--100代表反转正转
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);
    }
}

Motor.h

#ifndef __MOTOR_H
#define __MOTOR_H

void Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);

#endif

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;//每按一次加20速度
            if (Speed > 100)//如果速度大于100
            {
                Speed = -100;//反转
            }
        }
        Motor_SetSpeed(Speed);
        OLED_ShowSignedNum(1, 7, Speed, 3);
    }
}