news 2026/6/11 9:23:59

单片机直流有刷电机基础驱动实验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
单片机直流有刷电机基础驱动实验

单片机 :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); } }

实验结果:

电机可以运行。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 9:23:37

影刀RPA进阶教程_Windows桌面应用程序自动化操作指南

影刀RPA进阶教程&#xff1a;Windows桌面应用程序自动化操作指南 网页自动化是影刀的强项&#xff0c;但有时你需要操作桌面端程序——比如操作微信客户端、操作 WPS、操作 ERP 系统客户端。 桌面程序自动化和网页自动化思路不同&#xff1a;没有 DOM 树、没有 XPath、不能用…

作者头像 李华
网站建设 2026/6/11 9:23:03

如何在10分钟内彻底解决Citra模拟器黑屏闪退问题:终极完整指南

如何在10分钟内彻底解决Citra模拟器黑屏闪退问题&#xff1a;终极完整指南 【免费下载链接】citra A Nintendo 3DS Emulator 项目地址: https://gitcode.com/GitHub_Trending/ci/citra 你是否曾经满怀期待地打开Citra模拟器&#xff0c;准备重温经典的3DS游戏&#xff0…

作者头像 李华