一、基础知识

1. 步进电机控制方式

脉冲+方向控制(最常见)

控制信号:

        DIR方向:高低电平决定正转或反转;

        STEP脉冲:每个脉冲电机前进一步(可通过端口拉高拉低来模拟脉冲,或使用pwm来生成脉冲);

方法定义说明
GPIO 模拟脉冲用软件代码控制 GPIO 引脚“高/低”来模拟脉冲信号,通常通过 delay_us() 等手动延时
PWM 脉冲(定时器)使用硬件定时器自动生成一定频率和占空比的脉冲波形,直接输出到 GPIO 引脚

2. GPIO模拟脉冲和PWM生成脉冲两者的区别

项目GPIO 模拟脉冲PWM 定时器输出
控制方式纯软件控制:代码中手动翻转引脚硬件自动输出,CPU不再关心
精度/频率稳定性受 CPU、延时函数精度影响(不稳定)非常精准(定时器硬件级别)
CPU 占用率高:CPU 要一直跑在 delay 上低:设置一次,自动输出
适合任务简单、低速、临时用实时、高速、精确的脉冲控制
速度上限通常 <5kHz,超了容易乱可达几十 kHz 或更高
支持加减速控制复杂,需要自己调节 delay 变量更容易调节频率,甚至可做变频波(配合 ARR)
控制灵活性灵活(逐步手动控制每一脉冲)受限于定时器结构,但效率高

3. 驱动器细分档

        驱动器上的“细分挡位”是用于设置步进电机细分数(microstepping)的开关,它可以控制电机每收到一个STEP脉冲时的实际转动角度,以实现更平滑、更高精度的运动控制。

        普通步进电机的步距角通常是:1.8°(即每转动一圈需要200个整步)。

细分数每微步角度(1.8°电机)每圈脉冲数
1(全步)1.8°200
2(半步)0.9°400
40.45°800
80.225°1600
160.1125°3200
320.05625°6400
例如下面的驱动器,可以调节M1、M2、M3来控制细分档位。

二、实验

1. GPIO模拟脉冲方式

实验现象:正转2圈,延时,反转1圈。

main.c

#include "stm32f10x.h"
#include "usart1.h"
#include "delay.h"
#include "string.h"
#include "systick.h"
#include "led.h"
#include "motor.h"
#include "key.h"int main(void)
{SystemInit();delay_init();StepMotor_GPIO_Init();StepMotor_Enable();        // 开启电机LED_Init();Key_Init();StepMotor_SetDirection(1); // 设置方向while (1) {
//								if (GPIO_ReadInputDataBit(KEY_GPIO, KEY1_PIN) == Bit_RESET) // 按键1按下(低电平有效)
//								{
//										StepMotor_SetDirection(1);                  // 顺时针
//										StepMotor_StepPulse(200, 500);              // 200 步,800us 间隔(约625Hz)
//										LED_ON();
//								}
//								else if (GPIO_ReadInputDataBit(KEY_GPIO, KEY2_PIN) == Bit_RESET) // 按键2按下
//								{
//										StepMotor_SetDirection(0);                  // 顺时针
//										StepMotor_StepPulse(200, 500);              // 200 步,800us 间隔(约625Hz)
//									  LED2_ON();
//								}else{
//										LED_OFF();
//									  LED2_OFF();
//							  }StepMotor_TurnOneCircle(1, 2, 500); // 正转2圈,500us间隔(约1666Hz)delay_ms(8000);StepMotor_TurnOneCircle(0, 1, 500); // 反转一圈delay_ms(8000);}
}

motor.c

#include  "motor.h"void StepMotor_GPIO_Init(void) {RCC_APB2PeriphClockCmd(STEPMOTOR_RCC, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = STEP_PIN | DIR_PIN | EN_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(STEPMOTOR_GPIO, &GPIO_InitStructure);GPIO_ResetBits(STEPMOTOR_GPIO, STEP_PIN);GPIO_ResetBits(STEPMOTOR_GPIO, DIR_PIN);GPIO_SetBits(STEPMOTOR_GPIO, EN_PIN); // 默认使能(高电平)
}void StepMotor_Enable(void) {GPIO_SetBits(STEPMOTOR_GPIO, EN_PIN);  // 高电平 = 使能
}void StepMotor_Disable(void) {GPIO_ResetBits(STEPMOTOR_GPIO, EN_PIN); // 低电平 = 失能(断电)
}void StepMotor_SetDirection(uint8_t dir) {if (dir)GPIO_SetBits(STEPMOTOR_GPIO, DIR_PIN);elseGPIO_ResetBits(STEPMOTOR_GPIO, DIR_PIN);
}void StepMotor_StepPulse(uint32_t steps, uint32_t delay_us_val) {for (uint32_t i = 0; i < steps; i++) {GPIO_SetBits(STEPMOTOR_GPIO, STEP_PIN);   // STEP 上升沿delay_us(delay_us_val);GPIO_ResetBits(STEPMOTOR_GPIO, STEP_PIN); // 下降沿delay_us(delay_us_val);}
}void StepMotor_TurnOneCircle(uint8_t dir, uint16_t circles, uint32_t speed_us)
{StepMotor_SetDirection(dir);               // 设置方向(1=正转,0=反转)for (uint32_t i = 0; i < circles*1600; i++) {       // 1600步 = 一圈(8细分)GPIO_SetBits(GPIOA, STEP_PIN);         // STEP高delay_us(speed_us);GPIO_ResetBits(GPIOA, STEP_PIN);       // STEP低delay_us(speed_us);}
}// 简单延时函数(可使用SysTick或Timer)
void delay_us_function(uint32_t us)
{for (uint32_t i = 0; i < us * 8; i++)__NOP();
}

motor.h

#ifndef __MOTOR_H
#define __MOTOR_H#include "stm32f10x.h"// 步进电机引脚定义(你已说明:PA1 -> STEP,PA2 -> DIR,PA3 -> EN)
// GPIO 配置
#define STEPMOTOR_GPIO        GPIOA
#define STEPMOTOR_RCC         RCC_APB2Periph_GPIOA#define STEP_PIN              GPIO_Pin_1
#define DIR_PIN               GPIO_Pin_2
#define EN_PIN                GPIO_Pin_3void StepMotor_GPIO_Init(void);
void StepMotor_SetDirection(uint8_t dir);
void StepMotor_Enable(void);
void StepMotor_Disable(void);
void StepMotor_StepPulse(uint32_t steps, uint32_t delay_us_val);
void StepMotor_TurnCircle(uint8_t dir, uint16_t circles, uint32_t speed_us);
void delay_us(uint32_t us);#endif // __STEPMOTOR_H

key.c

#include "key.h"static uint8_t key1_last = 1;
static uint8_t key2_last = 1;void Key_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);GPIO_InitTypeDef gpio;// KEY1/KEY2 输入,带上拉gpio.GPIO_Pin = KEY1_PIN | KEY2_PIN;gpio.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(KEY_GPIO, &gpio);}uint8_t Key_Scan(void)
{uint8_t key1_now = GPIO_ReadInputDataBit(KEY_GPIO, KEY1_PIN);   //读取当前电平,0为按下。uint8_t key2_now = GPIO_ReadInputDataBit(KEY_GPIO, KEY2_PIN);uint8_t result = KEY_NONE;  //0,没有按键// 按键1:检测下降沿if (key1_last == 1 && key1_now == 0) {delay_ms(20);  // 消抖if (GPIO_ReadInputDataBit(KEY_GPIO, KEY1_PIN) == Bit_RESET)result = KEY1_PRESSED;    //result =1,表示按键1按下。}// 按键2:检测下降沿if (key2_last == 1 && key2_now == 0) {delay_ms(20);  // 消抖if (GPIO_ReadInputDataBit(KEY_GPIO, KEY2_PIN) == Bit_RESET)result = KEY2_PRESSED;   //result =2,表示按键1按下。}key1_last = key1_now;key2_last = key2_now;return result;
}

key.h

#ifndef __KEY_H
#define __KEY_H#include "stm32f10x.h"#define KEY1_PIN GPIO_Pin_1 // 正转按键   //PC1
#define KEY2_PIN GPIO_Pin_3 // 反转按键   //PC3
#define KEY_GPIO GPIOC// 返回值定义(按下事件)
#define KEY_NONE        0
#define KEY1_PRESSED    1
#define KEY2_PRESSED    2
void Key_Init(void);uint8_t Key_Scan(void);
#endif

2. pwm输出脉冲方式

实验现象:按下按键1电机正转2圈,按下按键2电机正转1圈。

tim2_pwm.c

#include "tim2_pwm.h"volatile uint32_t step_count;
volatile uint32_t target_steps;void StepMotor_PWM_Init(uint32_t freq_hz)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef gpio;gpio.GPIO_Pin = GPIO_Pin_1;gpio.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出gpio.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio);TIM_TimeBaseInitTypeDef tim;uint16_t prescaler = 72 - 1; // 假设主频72MHz → 1MHz定时器uint16_t period = 1000000 / freq_hz - 1; // 计算周期tim.TIM_Prescaler = prescaler;tim.TIM_CounterMode = TIM_CounterMode_Up;tim.TIM_Period = period;tim.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInit(TIM2, &tim);TIM_OCInitTypeDef oc;oc.TIM_OCMode = TIM_OCMode_PWM1;oc.TIM_OutputState = TIM_OutputState_Enable;oc.TIM_Pulse = period / 2; // 50% 占空比oc.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OC2Init(TIM2, &oc); // PA1 = CH2TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 开启更新中断NVIC_InitTypeDef nvic;nvic.NVIC_IRQChannel = TIM2_IRQn;nvic.NVIC_IRQChannelPreemptionPriority = 1;nvic.NVIC_IRQChannelSubPriority = 1;nvic.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&nvic);
}void StepMotor_RunCircles(uint32_t circles)
{step_count = 0;target_steps = circles*1600;TIM_Cmd(TIM2, ENABLE); // 启动PWM
}void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);step_count++;if (step_count >= target_steps){TIM_Cmd(TIM2, DISABLE); // 关闭PWMstep_count = 0;}}
}

main.c

#include "stm32f10x.h"
#include "usart1.h"
#include "delay.h"
#include "string.h"
#include "systick.h"
#include "led.h"
#include "motor.h"
#include "key.h"
#include "tim2_pwm.h"int main(void)
{SystemInit();delay_init();StepMotor_GPIO_Init();LED_Init();Key_Init();StepMotor_PWM_Init(1000);  // 设置1kHz频率StepMotor_Enable();while (1) {uint8_t key = Key_Scan();if (key == KEY1_PRESSED) {StepMotor_SetDirection(1);StepMotor_RunCircles(2);}else if (key == KEY2_PRESSED) {StepMotor_SetDirection(0);StepMotor_RunCircles(1);}}
}

注意:pwm输出这里有些函数是和GPIO公用的,比如引脚的初始化,这里我没有再去划分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/88349.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/88349.shtml
英文地址,请注明出处:http://en.pswp.cn/web/88349.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker 容器部署脚本

#!/bin/bash# # Author: ldj # Date: 2025-07-08 15:37:11 # Description: 首先删除旧的容器和镜像&#xff0c;然后登录到 Harbor 并拉取最新的镜像进行部署 # # 显示每条命令执行情况&#xff0c;便于调试 set -x harbor_addr$1 harbor_repo$2 project_name$3 version$4 po…

OpenCV 4.10.0 移植 - Android

前文: Ubuntu 编译 OpenCV SDK for Android Linux OpenCV 4.10.0 移植 概述 在移动应用开发领域&#xff0c;Android平台与OpenCV库的结合为开发者提供了强大的图像处理和计算机视觉能力。OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件…

go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南

go go go 出发咯 - go web开发入门系列&#xff08;二&#xff09; Gin 框架实战指南 往期回顾 go go go 出发咯 - go web开发入门系列&#xff08;一&#xff09; helloworld 前言 前一节我们使用了go语言简单的通过net/http搭建了go web服务&#xff0c;但是仅使用 Go 的标…

编译OpenHarmony-4.0-Release RK3566 报错

编译OpenHarmony-4.0-Release RK3566 报错1. 报错问题2.问题解决3.解决方案4.​调试技巧​subsystem name config incorrect in ‘/home/openharmony/OpenHarmony/vendor/kaihong/khdvk_356b/bundle.json’, build file subsystem name is kaihong_products,configured subsy1.…

【PTA数据结构 | C语言版】线性表循环右移

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 给定顺序表 A(a1​,a2​,⋯,an​)&#xff0c;请设计一个时间和空间上尽可能高效的算法将该线性表循环右移指定的 m 位。例如&#xff0c;(1,2,5,7,3,4,6,8) 循环右移 3 位&#xff08;m3) 后的结果…

c++-内部类

概念如果一个类定义在另一个类的内部&#xff0c;这个内部类就叫做内部类。内部类是一个独立的类&#xff0c; 它不属于外部类。特性1.不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。 2.内部类就是外部类的友元类&#xff0c;参见友元类的定…

.golangci.yml文件配置

version: “2” run: timeout: 5m concurrency: 10 modules-download-mode: readonly linters: default: standard enable: - revive - cyclop settings: staticcheck: initialisms: [ “ACL”, “API”, “ASCII”, “CPU”, “CSS”, “DNS”, “EOF”, “GUID”, “HTML”, …

YOLO模型魔改指南:从原理到实战,替换Backbone、Neck和Head(战损版)

前言 Hello&#xff0c;大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列是作者参加DataWhale 2025年6月份Yolo原理组队学习的技术笔记文档&#xff0c;这里整理为博客&#xff0c;希望能帮助Yolo的开发者少走弯路&#xff01; &am…

Swift 图论实战:DFS 算法解锁 LeetCode 323 连通分量个数

文章目录摘要描述示例题解答案DFS 遍历每个连通区域Union-Find&#xff08;并查集&#xff09;题解代码分析&#xff08;Swift 实现&#xff1a;DFS&#xff09;题解代码详解构建邻接表DFS 深度优先搜索遍历所有节点示例测试及结果示例 1示例 2示例 3时间复杂度分析空间复杂度分…

【剑指offer】栈 队列

&#x1f4c1; JZ9 用两个栈实现队列一个栈in用作进元素&#xff0c;一个栈out用于出元素。当栈out没有元素时&#xff0c;从in栈获取数据&#xff0c;根据栈的特性&#xff0c;栈out的top元素一定是先进入的元素&#xff0c;因此当栈out使用pop操作时&#xff0c;一定时满足队…

GoView 低代码数据可视化

纯前端 分支&#xff1a; master &#x1f47b; 携带 后端 请求分支: master-fetch &#x1f4da; GoView 文档 地址&#xff1a;https://www.mtruning.club/ 项目纯前端-Demo 地址&#xff1a;https://vue.mtruning.club/ 项目带后端-Demo 地址&#xff1a;https://demo.mtrun…

Spring Boot返回前端Long型丢失精度 后两位 变成00

文章目录一、前言二、问题描述2.1、问题背景2.2、问题示例三、解决方法3.1、将ID转换为字符串3.2、使用JsonSerialize注解3.3、使用JsonFormat注解一、前言 在后端开发中&#xff0c;我们经常会遇到需要将ID作为标识符传递给前端的情况。当ID为long类型时&#xff0c;如果该ID…

计算机网络实验——无线局域网安全实验

实验1. WEP和WPA2-PSK实验一、实验目的验证AP和终端与实现WEP安全机制相关的参数的配置过程。验证AP和终端与实现WPA2-PSK安全机制相关的参数的配置过程。验证终端与AP之间建立关联的过程。验证关闭端口的重新开启过程。验证属于不同BSS的终端之间的数据传输过程。二、实验任务…

【从零开始学Dify】大模型应用开发平台Dify本地化部署

目录Dify一、本地化部署1、安装docker2、安装Dify&#xff08;1&#xff09;拉取代码到本地&#xff08;2&#xff09;docker部署&#xff08;3&#xff09;查看服务状态&#xff08;4&#xff09;web端部署&#xff08;5&#xff09;登录二、可能会出现的问题&#xff08;1&am…

LVGL应用和部署(和物理按键交互)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】屏幕除了显示部分&#xff0c;还要去和其他外设进行交互&#xff0c;这是非常重要的一个处理方法。我们知道&#xff0c;不管是mcu&#xff0c;还是…

限流式保护器如何筑牢无人驾驶汽车充电站的安全防线

摘要&#xff1a; 随着新能源汽车&#xff0c;尤其是无人驾驶车队的快速发展&#xff0c;充电设施的安全可靠性至关重要。交流充电桩&#xff08;俗称“慢充桩”&#xff09;作为重要的充电基础设施&#xff0c;其末端回路的安全保护需满足国家标准GB51348-2019的严格要求&…

专题:2025母婴行业洞察报告|附60+份报告PDF汇总下载

原文链接&#xff1a;https://tecdat.cn/?p42908 全球母婴市场正经历结构性增长&#xff0c;一面是欧美成熟市场的品质消费升级&#xff0c;一面是东南亚、中东等新兴市场的人口红利释放。2020至2026年&#xff0c;全球母婴市场规模将从1859亿美元增至3084亿美元&#xff0c;年…

从零搭建多商户商城系统源码:技术栈、数据库设计与接口规划详解

如今&#xff0c;多商户商城系统已成为传统零售转型与新型电商平台构建的关键利器。无论是打造像某宝、某东这样的综合型平台&#xff0c;还是服务于垂直行业的独立电商&#xff0c;一套高效、可扩展的多商户商城系统源码&#xff0c;往往决定着平台的成败。 今天&#xff0c;小…

在Docker中运行macOS的超方便体验!

在数字化和开发人员快速迭代的今日&#xff0c;拥有一个便捷、高效的开发环境成为每个开发者梦寐以求的事情。特别是在需要操作多个系统、开发跨平台应用时&#xff0c;调试和测试的便利性显得尤为重要。今天为大家介绍的这款开源项目&#xff0c;正是一个解决此类问题的利器—…

Kettle导入Excel文件进数据库时,数值发生错误的一种原因

1、问题描述及原因 在使用kettle读取Excel文件、并导入数据库时&#xff0c;需要读取Excel中的数值、日期(或日期时间、时间)、文本这三种类型的列进来&#xff0c;发现读取其中的数值时&#xff0c;读取的数字就不对。 经调查&#xff0c;原因是&#xff0c;在“导出数据为E…