STM32F1控制步進電機

一、基礎知識

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…