草庐IT

STM32F103步进电机驱动简单控制

小昌快跑 2023-04-11 原文

stm32f103步进电机驱动控制

一、硬件接线方法

本文采用混合式二相步进电机-J8HB2401-野火42步进电机,驱动器为野火EBF-MSD4805,下面是常用接线方式:
如上图所示通常采用共阴接线方式,具体接线按照自己需求进行完成。
另外二相电机步距角1.8°,步进驱动器侧面SW1-SW8分别为细分设定、电流设定和驱动模式选择开关。

二、程序

代码如下(示例):

1.main.c

#include "stm32f10x.h"
#include "tim.h"
#include "bsp_usart.h"
#include "stm32f10x_it.h"
#include "stm32f10x_conf.h"
/* 共阴 */
/* 
PUL+->PA.1  PUL-接GND
DIR+->PA.8  DIR-接GND
*/
extern int motor_dir2;
extern int motor_speed;

int main(void)
{	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	
	USART_Config();	     	
	Driver_Init();/* GPIO_Init */			
	TIM2_Init(999,72-1);       
	while(1) 
	{	
		Locate_Rle2(motor_speed,motor_dir2);/* 变速以及方向 */	
	}
}

2.tim.c

#include "tim.h"
#include "bsp_usart.h"
 
long current_pos[2] = {0,0}; 
int motor_dir2 = 0;
u8 count[2] = {0,0};
u32 motor_speed = 0;
u8 rcr_remainder;     //重复计数余数部分
u8 is_rcr_finish = 1;   //重复计数器是否设置完成
long rcr_integer;	  //重复计数整数部分
long target_pos = 0;    //有符号方向

/* 电机转速与方向函数 */
void Locate_Rle2(u32 frequency,DIR_Type dir) 
{
	motor_dir2 = dir;
	
	DRIVER_DIR = motor_dir2;
	if(motor_dir2 == CW){motor_speed = 3000;}/* motor_dir2高电平时转速3000Hz */
	else if(motor_dir2 == CCW){motor_speed = 10000;}/* motor_dir2低电平时转速10000Hz */
	frequency = motor_speed;
	
	if(TIM2->CR1&0x01)
	{
		printf("\r\nThe last time pulses is not send finished,wait please!\r\n");
		return;
	}
	if((frequency<20)||(frequency>100000))
	{
		printf("\r\nThe frequency is out of range! please reset it!!(range:20Hz~100KHz)\r\n");
		return;
	}
	TIM2_Startup(frequency);			
}

/* PA8 - DIR+*/
void Driver_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(GPIOA, &GPIO_InitStructure);					
	GPIO_SetBits(GPIOA,GPIO_Pin_8);						
}
 
/* PA1- PUL+ TIM2_CH2 */
void TIM2_Init(u16 arr,u16 psc)
{		 					 
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);                                                                      	
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;				
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
	
	TIM_TimeBaseStructure.TIM_Period = arr;					 
	TIM_TimeBaseStructure.TIM_Prescaler =psc;				
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; 			
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 		
	TIM_ClearITPendingBit(TIM2,TIM_IT_Update);				
 
	TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Regular); 
	TIM_SelectOnePulseMode(TIM2,TIM_OPMode_Single);			
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;		
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 
	TIM_OCInitStructure.TIM_Pulse = arr>>1; 				
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
	TIM_OC2Init(TIM2, &TIM_OCInitStructure); 				
 
	TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);  		
	TIM_ARRPreloadConfig(TIM2, ENABLE); 					
	
	TIM_ITConfig(TIM2, TIM_IT_Update ,ENABLE);  			
 
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  		
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 	
	NVIC_Init(&NVIC_InitStructure);  						
	
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  			
	TIM_Cmd(TIM2, DISABLE);  															  
}

 
void TIM2_Startup(u32 frequency)   
{
	u16 temp_arr=1000000/frequency-1; 
	TIM_SetAutoreload(TIM2,temp_arr);
	TIM_SetCompare2(TIM2,temp_arr>>1); 
	
	TIM_SetCounter(TIM2,0);
	TIM_Cmd(TIM2, ENABLE);  
}

3.tim.h

#include "stm32f10x.h"
#include "stdlib.h"

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
#define PAout(n)          BIT_ADDR(GPIOA_ODR_Addr,n) 
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) 
#define DRIVER_DIR        PAout(8) 

typedef enum
{
	CW = 1,//高电平顺时针
	CCW = 0,//低电平逆时针
}DIR_Type;

void Driver_Init(void);
void TIM2_Init(u16 arr,u16 psc);
void TIM2_Startup(u32 frequency);
void Locate_Rle2(u32 frequency,DIR_Type dir);

4.stm32f10x_it.c

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
#include "tim.h"
#include "bsp_usart.h"
#include "systick.h"

extern u8 count[2];
extern long current_pos[2]; //有符号方向
extern int motor_dir2;
int counter = 0;

/******* TIM2 - 1ms*********/
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2,TIM_FLAG_Update)!=RESET)
	{
		TIM_ClearITPendingBit(TIM2,TIM_FLAG_Update);	
		count[0]++;
		counter ++;
		TIM_GenerateEvent(TIM2,TIM_EventSource_Update);
	
		TIM_Cmd(TIM2, ENABLE);  					  
 
		if(count[0]==200)
		{
			if(motor_dir2==CW) 						  
				current_pos[0]+=count[0];
			else          							  
				current_pos[0]-=count[0];
			TIM_Cmd(TIM2, DISABLE);  				  		
			printf("\r\n motor2=%ld\r\n",current_pos[0]);
			count[0]=0;
		}
		
		if(counter <= 5000){motor_dir2=CW;}/* 定时器前5s拉高motor_dir2电平 */
		else if((counter > 5000) && (counter <= 10000)){motor_dir2=CCW;}/* 拉低电平 */
		else if(counter > 10000){counter=0;}/* 复位 */
	}
}

a.中断函数可以从保准库中进行拷贝,具体采用哪一个串口自己需要对照原理图;
b.PAout(n)函数采用GPIO-位带操作, 把“位带地址+ 位序号”转换成别名地址宏统一公式如下,详见野火指南:
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x02000000+((addr & 0x00FFFFFF)<<5)+(bitnum<<2))


总结

很多网友例程都对PAout(n)函数为加讲解,初学者不太容易get;另外在很多博主主函数或者定时器函数中都采用delay()函数进行延时操作是不对的,因为MCU在delay时间内无法进行其他动作。

展望

后续会更新升降电机过程中的梯形、S型加减速,相对,绝对,回原点,PID调节。

有关STM32F103步进电机驱动简单控制的更多相关文章

  1. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  2. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  3. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  4. STM32的HAL和LL库区别和性能对比 - 2

    LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L

  5. ruby - 运行测试时静音 Chrome 驱动程序控制台输出 - 2

    我使用的是最新版本的Chrome(32.0.1700.107)和Chrome驱动程序(V2.8)。但是当我在Ruby中使用以下代码运行示例测试时:require'selenium-webdriver'WAIT=Selenium::WebDriver::Wait.new(timeout:100)$driver=Selenium::WebDriver.for:chrome$driver.manage.window.maximize$driver.navigate.to'https://www.google.co.in'defapps_hoverele_hover=$driver.find_

  6. node.js - 从未编写过任何自动化测试,我应该如何开始行为驱动开发? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。多年来,我一直在使用多种语言进行编程,并且认为自己总体上相当擅长。但是,我从未编写过任何自动化测试:没有单元测试,没有TDD,没有BDD,什么都没有。我已经尝试开始为我的项目编写适当的测试套件。我可以看到在进行任何更改后能够自动测试项目中所有代码的理论值(value)。我可以看到像RSpec和Mocha这样的测试框架应该如何使设置和运行所述测试变得相当容易

  7. ruby-on-rails - 在 Rails/Capybara/Poltergeist 规范中使用 url_for 将驱动程序发送到 example.com 而不是应用程序 - 2

    如果我在功能规范中调用url_for,它会返回一个以http://www.example.com/开头的绝对URL.Capybara会很乐意尝试加载该站点上的页面,但这与我的应用程序无关。以下是重现该问题的最少步骤:从这个Gemfile开始:source'https://rubygems.org'gem"sqlite3"gem"jquery-rails"gem"draper"gem"rails",'4.1.0'gem"therubyracer"gem"uglifier"gem"rspec-rails"gem"capybara"gem"poltergeist"gem"launchy"运行

  8. 驱动开发:内核无痕隐藏自身分析 - 2

    在笔者前面有一篇文章《驱动开发:断链隐藏驱动程序自身》通过摘除驱动的链表实现了断链隐藏自身的目的,但此方法恢复时会触发PG会蓝屏,偶然间在网上找到了一个作者介绍的一种方法,觉得有必要详细分析一下他是如何实现的进程隐藏的,总体来说作者的思路是最终寻找到MiProcessLoaderEntry的入口地址,该函数的作用是将驱动信息加入链表和移除链表,运用这个函数即可动态处理驱动的添加和移除问题。MiProcessLoaderEntry(pDriverObject->DriverSection,1)添加MiProcessLoaderEntry(pDriverObject->DriverSection,

  9. ESP32学习入门:WiFi连接网络 - 2

    目录一、ESP32简单介绍二、ESP32Wi-Fi模块介绍三、ESP32Wi-Fi编程模型四、ESP32Wi-Fi事件处理流程 五、ESP32Wi-Fi开发环境六、ESP32Wi-Fi具体代码七、ESP32Wi-Fi代码解读6.1主程序app_main7.2自定义代码wifi_init_sta()八、ESP32Wi-Fi连接验证8.1测试方法8.2服务器模拟工具sscom58.3测试代码8.4测试结果前言为了开发一款亚马逊物联网产品,开始入手ESP32模块。为了能够记录自己的学习过程,特记录如下操作过程。一、ESP32简单介绍ESP32是一套Wi-Fi(2.4GHz)和蓝牙(4.2)双模解决方

  10. Spring Security 6.0系列【32】授权服务器篇之默认过滤器 - 2

    有道无术,术尚可求,有术无道,止于术。本系列SpringBoot版本3.0.4本系列SpringSecurity版本6.0.2本系列SpringAuthorizationServer版本1.0.2源码地址:https://gitee.com/pearl-organization/study-spring-security-demo文章目录前言1.OAuth2AuthorizationServerMetadataEndpointFilter2.OAuth2AuthorizationEndpointFilter3.OidcProviderConfigurationEndpointFilter4.N

随机推荐