单片机  :STM32F407
开发板:DMF407电机开发板
平台:keil   V5.31

HSE 为8MHZ
HSI为16MHZ

原理图:

有刷直流电机H桥(H-Bridge)驱动电路‌是控制有刷直流电机正反转、调速、刹车的经典电路,因电路形状酷似字母"H"得名,被广泛应用于机器人、智能小车、电动工具等场景。

标准H桥由4个独立控制的开关元件(通常为MOSFET或三极管)和电机组成,电机架在两个并联支路中间形成"桥"结构,通过控制不同开关的通断改变电流方向,实现不同工作状态:

  1. 正转‌:导通左上(S1)和右下(S4)开关,关闭另外两个开关。电流路径:电源→S1→电机→S4→地,电流从左向右流过电机,电机正转。
  2. 反转‌:导通右上(S3)和左下(S2)开关,关闭另外两个开关。电流路径:电源→S3→电机→S2→地,电流反向流过电机,电机反转。
  3. 刹车‌:动态刹车时导通同侧两个开关(如S1和S3),将电机两端短接,电机旋转产生的反电动势形成制动力矩,可快速停止;滑行刹车则关闭所有开关,电机依靠惯性自由停止。
  4. 调速‌:在正转/反转状态下,通过‌脉冲宽度调制(PWM)‌ 调节开关的导通占空比,改变电机两端的平均电压,从而控制转速,占空比越高转速越快。

双半桥结构:

完整双极性H桥驱动可使用‌一个高级定时器的1个通道+另一个半桥复用相同逻辑‌实现,具体配置要点:

  1. 定时器基础参数‌:选择高级定时器(STM32选TIM1/TIM8),设置时钟预分频和自动重装载值,得到1kHz~20kHz的PWM频率,满足电机调速需求。PWM频率计算公式:PWM频率 = 定时器时钟 / [(预分频值+1) × (自动重装载值+1)]
  2. 通道输出配置‌:
    • 通道1设置为PWM模式1,开启主输出CH1和互补输出CH1N,死区时间建议设置为500ns~1μs(可根据开关管型号调整)。
    • CH1驱动左侧半桥下管,CH1N驱动左侧半桥上管,完成第一个半桥驱动。
    • 右侧半桥通过GPIO翻转控制电平逻辑,配合左侧半桥即可实现电流方向切换,控制电机正反转,调速通过单通道PWM占空比调节实现。

半桥结构:

直流有刷电机半桥驱动是结构简单的基础驱动方案,仅需半个H桥电路即可实现,适合仅需单向调速、对成本敏感的应用场景。

半桥驱动核心为两个串联的功率开关器件(一般为MOSFET或IGBT),直流有刷电机接在半桥中点与电源地之间,核心元件清单如下:

  • 功率开关‌:上管Q1(高侧开关,接电源正极)+下管Q2(低侧开关,接地)
  • 驱动控制‌:半桥驱动芯片(如IR2104),提供电平转换与死区控制
  • 外围元件‌:自举电容+自举二极管(给高侧MOS管提供驱动电压)、栅极限流电阻、反激续流二极管

通过两个开关交替导通,通过PWM脉冲宽度调制控制电机平均电压,实现调速:

  1. 调速原理‌:当PWM为高电平时‌上管Q1导通、下管Q2关断‌,电源给电机供电,电流流过电机,电机加速;当PWM为低电平时‌上管Q1关断、下管Q2导通‌,电机断电,电流通过下管续流,电机依靠惯性运行。通过改变PWM占空比即可改变电机平均输入电压,从而调节转速。
  2. 刹车功能‌:保持下管持续导通即可实现动态刹车,电机绕组被短路,依靠反电动势快速停止,相比单开关驱动方案刹车响应更快。
  3. 方向限制‌:传统单半桥结构仅能提供单向电流,因此电机只能单向转动;如果需要正反转,必须使用两个半桥组成完整的H桥驱动。

根据官方的资料:

假设我们让 TIM1_CH1(主通道)输出 PWM 波,TIM1_CH1N(互补通道)固定输出高
电平,此时只要调节主通道输出的 PWM 占空比即可调整电机上的电压,进而控制电机的速
度。当电机需要换向的时候,我们就让主通道固定输出高电平,互补通道输出 PWM 即可。
主函数:
int main(void)
{     
    uint8_t key,t;
    int32_t motor_pwm = 0;
    
    HAL_Init();                              /* 初始化HAL库 */
    sys_stm32_clock_init(336, 8, 2, 7);      /* 设置时钟,168Mhz */
    delay_init(168);                         /* 延时初始化 */
    usart_init(115200);                      /* 串口初始化为115200 */
    led_init();                              /* 初始化LED */
    lcd_init();                              /* 初始化LCD */
    key_init();                              /* 初始化按键 */
    atim_timx_cplm_pwm_init(8400 - 1, 0);    /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
    dcmotor_init();                          /* 初始化电机 */
    
    g_point_color = WHITE;
    g_back_color  = BLACK;
    lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
    lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
    lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
    lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
    
    printf("KEY0:增加比较值,KEY1:减小比较值,KEY2:停止电机\r\n");
    
    while (1)
    {
        key = key_scan(0);                  /* 按键扫描 */
        if(key == KEY0_PRES)                /* 当key0按下 */
        {
            motor_pwm += 400;               /* 因为不同的电机最小启动电压不同,可能在第一次增加的时候电机还不能转起来 */
            
            if (motor_pwm == 0) 
            {
                dcmotor_stop();             /* 停止则立刻响应 */
                motor_pwm = 0;
            } 
            else 
            {
                dcmotor_start();            /* 开启电机 */
                if (motor_pwm >= 8400)      /* 限速 */
                {
                    motor_pwm = 8400;
                }               
            }
            motor_pwm_set(motor_pwm);       /* 设置电机PWM的占空比 */
        }
        
        else if(key == KEY1_PRES)                /* 当key1按下 */
        {
            motor_pwm -= 400;
            if (motor_pwm == 0) 
            {
                dcmotor_stop();             /* 停止则立刻响应 */
                motor_pwm = 0;
            } 
            else 
            {
                dcmotor_start();            /* 开启电机 */
                if (motor_pwm <= -8400)     /* 限速 */
                {
                    motor_pwm = -8400;
                }               
            }
            motor_pwm_set(motor_pwm);       /* 设置电机PWM的占空比 */
        }
        
        else if(key == KEY2_PRES)                /* 当key2按下 */
        {
            LED1_TOGGLE();
            dcmotor_stop();                 /* 关闭电机 */
            motor_pwm = 0;
            motor_pwm_set(motor_pwm);       /* 设置电机PWM的占空比 */
        }
        
        delay_ms(10);
        t++;
        if(t % 20 == 0)
        {
            LED0_TOGGLE();                  /*LED0(红灯) 翻转*/
        }
    }
}

电机部分

#define SHUTDOWN1_Pin                 GPIO_PIN_10
#define SHUTDOWN1_GPIO_Port           GPIOF

#define SHUTDOWN2_Pin                 GPIO_PIN_2
#define SHUTDOWN2_GPIO_Port           GPIOF
#define SHUTDOWN_GPIO_CLK_ENABLE()    do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)   /* PF口时钟使能 */

/* 电机停止引脚定义 这里默认是接口1 */
#define ENABLE_MOTOR    HAL_GPIO_WritePin(SHUTDOWN1_GPIO_Port,SHUTDOWN1_Pin,GPIO_PIN_SET)
#define DISABLE_MOTOR   HAL_GPIO_WritePin(SHUTDOWN1_GPIO_Port,SHUTDOWN1_Pin,GPIO_PIN_RESET)

void dcmotor_init(void)
{
    SHUTDOWN_GPIO_CLK_ENABLE();
    GPIO_InitTypeDef gpio_init_struct;
    
    /* SD引脚设置,设置为推挽输出 */
    gpio_init_struct.Pin = SHUTDOWN1_Pin|SHUTDOWN2_Pin;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(SHUTDOWN1_GPIO_Port, &gpio_init_struct);
    
    HAL_GPIO_WritePin(GPIOF, SHUTDOWN1_Pin|SHUTDOWN2_Pin, GPIO_PIN_RESET);      /* SD拉低,关闭输出 */
    
    dcmotor_stop();                 /* 停止电机 */
    dcmotor_dir(0);                 /* 设置正转 */
    dcmotor_speed(0);               /* 速度设置为0 */
    dcmotor_start();                /* 开启电机 */
}

void dcmotor_start(void)
{
    ENABLE_MOTOR;                                                       /* 拉高SD引脚,开启电机 */
}

void dcmotor_stop(void)
{
    HAL_TIM_PWM_Stop(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);          /* 关闭主通道输出 */
    HAL_TIMEx_PWMN_Stop(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);       /* 关闭互补通道输出 */
    DISABLE_MOTOR;                                                      /* 拉低SD引脚,停止电机 */
}

void dcmotor_dir(uint8_t para)
{
    HAL_TIM_PWM_Stop(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);          /* 关闭主通道输出 */
    HAL_TIMEx_PWMN_Stop(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);       /* 关闭互补通道输出 */

    if (para == 0)                /* 正转 */
    {
        HAL_TIM_PWM_Start(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);     /* 开启主通道输出 */
    } 
    else if (para == 1)           /* 反转 */
    {
        HAL_TIMEx_PWMN_Start(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1);  /* 开启互补通道输出 */
    }
}

void dcmotor_speed(uint16_t para)
{
    if (para < (__HAL_TIM_GetAutoreload(&g_atimx_cplm_pwm_handle) - 0x0F))  /* 限速 */
    {  
        __HAL_TIM_SetCompare(&g_atimx_cplm_pwm_handle, TIM_CHANNEL_1, para);
    }
}

void motor_pwm_set(float para)
{
    int val = (int)para;

    if (val >= 0) 
    {
        dcmotor_dir(0);           /* 正转 */
        dcmotor_speed(val);
    } 
    else 
    {
        dcmotor_dir(1);           /* 反转 */
        dcmotor_speed(-val);
    }
}

定时器

/* TIMX 互补输出模式 定义 */

/* 主输出通道引脚 */
#define ATIM_TIMX_CPLM_CHY_GPIO_PORT            GPIOA
#define ATIM_TIMX_CPLM_CHY_GPIO_PIN             GPIO_PIN_8
#define ATIM_TIMX_CPLM_CHY_GPIO_CLK_ENABLE()    do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */

/* 互补输出通道引脚 */
#define ATIM_TIMX_CPLM_CHYN_GPIO_PORT           GPIOB
#define ATIM_TIMX_CPLM_CHYN_GPIO_PIN            GPIO_PIN_13
#define ATIM_TIMX_CPLM_CHYN_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */

/* TIMX 引脚复用设置
 * 因为PA8/PB13, 默认并不是TIM1的功能脚, 必须开启复用,PA8/PB13才能用作TIM1的功能
 */
#define ATIM_TIMX_CPLM_CHY_GPIO_AF              GPIO_AF1_TIM1

/* 互补输出使用的定时器 */
#define ATIM_TIMX_CPLM                          TIM1
#define ATIM_TIMX_CPLM_CHY                      TIM_CHANNEL_1
#define ATIM_TIMX_CPLM_CLK_ENABLE()             do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0)    /* TIM1 时钟使能 */

void atim_timx_cplm_pwm_init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef sConfigOC ;
    TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;

    g_atimx_cplm_pwm_handle.Instance = ATIM_TIMX_CPLM;                      /* 定时器x */
    g_atimx_cplm_pwm_handle.Init.Prescaler = psc;                           /* 定时器预分频系数 */
    g_atimx_cplm_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;          /* 向上计数模式 */
    g_atimx_cplm_pwm_handle.Init.Period = arr;                              /* 自动重装载值 */
    g_atimx_cplm_pwm_handle.Init.RepetitionCounter = 0;                     /* 重复计数器寄存器为0 */
    g_atimx_cplm_pwm_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;        /* 使能影子寄存器TIMx_ARR */
    HAL_TIM_PWM_Init(&g_atimx_cplm_pwm_handle) ;

    /* 设置PWM输出 */
    sConfigOC.OCMode = TIM_OCMODE_PWM1;                                     /* PWM模式1 */
    sConfigOC.Pulse = 0;                                                    /* 比较值为0 */
    sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;                              /* OCy 低电平有效 */
    sConfigOC.OCNPolarity = TIM_OCNPOLARITY_LOW;                            /* OCyN 低电平有效 */
    sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;                               /* 使用快速模式 */
    sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;                          /* 主通道的空闲状态 */
    sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;                        /* 互补通道的空闲状态 */
    HAL_TIM_PWM_ConfigChannel(&g_atimx_cplm_pwm_handle, &sConfigOC, ATIM_TIMX_CPLM_CHY);    /* 配置后默认清CCER的互补输出位 */   
    
    /* 设置死区参数,开启死区中断 */
    sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;                 /* OSSR设置为1 */
    sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;               /* OSSI设置为0 */
    sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;                     /* 上电只能写一次,需要更新死区时间时只能用此值 */
    sBreakDeadTimeConfig.DeadTime = 0X0F;                                   /* 死区时间 */
    sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;                    /* BKE = 0, 关闭BKIN检测 */
    sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW;             /* BKP = 1, BKIN低电平有效 */
    sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;     /* 使能AOE位,允许刹车后自动恢复输出 */
    HAL_TIMEx_ConfigBreakDeadTime(&g_atimx_cplm_pwm_handle, &sBreakDeadTimeConfig);         /* 设置BDTR寄存器 */

}

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == ATIM_TIMX_CPLM)
    {
        GPIO_InitTypeDef gpio_init_struct;
        
        ATIM_TIMX_CPLM_CHY_GPIO_CLK_ENABLE();   /* 通道X对应IO口时钟使能 */
        ATIM_TIMX_CPLM_CHYN_GPIO_CLK_ENABLE();  /* 互补通道对应IO口时钟使能 */
        ATIM_TIMX_CPLM_CLK_ENABLE();            /* 定时器x时钟使能 */

        /* 配置PWM主通道引脚 */
        gpio_init_struct.Pin = ATIM_TIMX_CPLM_CHY_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
        gpio_init_struct.Pull = GPIO_NOPULL;
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH ;
        gpio_init_struct.Alternate = ATIM_TIMX_CPLM_CHY_GPIO_AF;                /* 端口复用 */
        HAL_GPIO_Init(ATIM_TIMX_CPLM_CHY_GPIO_PORT, &gpio_init_struct);

        /* 配置PWM互补通道引脚 */
        gpio_init_struct.Pin = ATIM_TIMX_CPLM_CHYN_GPIO_PIN;
        HAL_GPIO_Init(ATIM_TIMX_CPLM_CHYN_GPIO_PORT, &gpio_init_struct);
    }
}

实验结果:

电机可以运行。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐