利用STM32F103精确控制步进电机

您所在的位置:网站首页 plc步进电机控制程序流程图 利用STM32F103精确控制步进电机

利用STM32F103精确控制步进电机

2024-07-09 08:33| 来源: 网络整理| 查看: 265

利用STM32F103精确控制步进电机 一、用到的元器件

STM32F103C8T6 42步进电机(42SHDC4040-17B) TB6600电机驱动器升级版

二、42步进电机 1.步进电机的基本知识

(1)拍数——每一次循环所包含的通电状态数(电机转过一个齿距角所需脉冲数) (2)单拍制分配方式——状态数=相数 (3)双拍制分配方式——状态数=相数的两倍 (4)步距角 ——步进机通过一个电脉冲转子转过的角度 计算公式

N:一个周期的运行拍数 Zr:转子齿数 拍数:N=km m:相数 k=1单拍制 k=2双拍制

(5)转速 转速 (6)角度细分的原理

电磁力的大小与绕组通电电流的大小有关。当通电相的电流并不马上升到位,而断电相的电流也非立即降为0时,它们所产生的磁场合力,会使转子有一个新的平衡位置,这个新的平衡位置是在原来的步距角范围内。 2. 42步进电机参数

无法查找到42SHDC4040-17B型号的详细资料,以通用42步进电机为例: 步距角 1.8° 步距角精度 ±5% 相数 2相 励磁方式 混合式 转子齿数 50 拍制 双拍制 其他参数:无 由步距角=1.8°推算出转子齿数为50,拍制为双拍制

3. 42步进电机速度与角度控制

电机的转速与脉冲频率成正比,电机转过的角度与脉冲数成正比。所以控制脉冲数和脉冲频率就可以精确调速。 理论上步进电机转速 = 频率 * 60 /((360/T)*x) w = f ∗ 60 ( 360 ° T × x ) w= \frac{f*60}{(\frac {360°}{T}\times x)} w=(T360°​×x)f∗60​

转速单位: 转/ 分频率单位:赫兹x 细分倍数T 步距角 例如,在本实验中,32细分;频率72000 赫兹;步距角1.8°;套用公式 72000 ∗ 60 ( ( 360 / 1.8 ) ∗ 32 ) = 112.5 \frac{72000*60}{((360/1.8)*32)}=112.5 ((360/1.8)∗32)72000∗60​=112.5rad/ min,即1.875 rad/s. 42步进电机结构图42步进电机结构图 三、TB6600电机驱动器升级版参数

TB6600步进电机驱动器升级版是一款专业的两相步进电机驱动,可实现正反转控制。通过S1,S2,S3 3位拨码开关选择7档细分控制(1,2/A,2/B,4,8,16,32,),通过S4,S5,S6 3位拨码开关选择8 档电流控制(0.5A,1A,1.5A,2A,2.5A,2.8A,3.0A,3.5A)。适合驱动57,42 型两相、四相混合式步进电机。

1.信号输入端

PUL+:脉冲信号输入正。( CP+ ) PUL-:脉冲信号输入负。( CP- ) DIR+:电机正、反转控制正。 DIR-:电机正、反转控制负。 EN+:电机脱机控制正。 EN-:电机脱机控制负。

共阳极接法:分别将PUL+,DIR+,EN+连接到控制系统的电源上, 如果此电源是+5V则可直接接入,如果此电源大于+5V,则须外部另加限流电阻R,保证给驱动器内部光藕提供8—15mA 的驱动电流。共阴极接法:分别将 PUL-,DIR-,EN-连接到控制系统的地端;脉冲输入信号通过PUL+接入,方向信号通过DIR+接入,使能信号通过EN+接入。若需限流电阻,限流电阻R的接法取值与共阳极接法相同。注:EN端可不接,EN有效时电机转子处于自由状态(脱机状态),这时可以手动转动电机转轴。 2.电机绕组连接

A+:连接电机绕组A+相。 A-:连接电机绕组A-相。 B+:连接电机绕组B+相。 B-:连接电机绕组B-相。

3.电源电压连接

VCC:电源正端“+” GND:电源负端“-” 注意:DC直流范围:9-32V。不可以超过此范围,否则会无法正常工作甚至损坏驱动器.

4.拨码开关 Micro step脉冲/转S1S2S3NCNCONONON1200ONONOFF2/A400ONOFFON2/B400OFFONON4800ONOFFOFF81600OFFONOFF163200OFFOFFON326400OFFOFFOFF 电流大小设定 Current(A)S4S5S60.5ONONON1.0ONOFFON1.5ONONOFF2.0ONOFFOFF2.5OFFONON2.8OFFOFFON3.0OFFONOFF3.5OFFOFFOFF 三、STM32F103

------说明:引脚部分在文章末尾有解释--------

1.引脚连接

A 0——PUL+ A 3——KEY1——V3 A12——DIR+ A11——EAN+ GND——EAN- ——KEY0

2.引脚功能

A0控制电机转速 A3控制按键 A9 A11 A11控制电机是否为锁死状态 A12控制电机正反转

3.定时器

1.本实验利用定时器TIM2和定时器TIM3构造一个主从定时器,TIM2作为主定时器控制电机的转速,TIM3作为从定时器控制电机的转动角度。 2.电机的转速和转角还与驱动器自身的细分数有关,但是驱动器细分数是通过影响电机的步距角来影响转速和转角,而TIM2和TIM3是控制步进电机的频率和脉冲数来控制转速转角 3.电机的转速和角度与定时器的关系(在不考虑电机自身的细分数下)

设TIM2的定时周期(即重装值)为nPDTemp2,预分频值为OCPolarity2  TIM3的定时周期(即重装值)为nPDTemp3,预分频值为OCPolarity3, 则单片机产生一个脉冲所需要的时间为: T = ( n P D T e m p 2 + 1 ) 72 M H z O C P o l a r i t y 2 + 1 T= \frac{(nPDTemp2+1)}{ \frac{72MHz}{OCPolarity2+1}} T=OCPolarity2+172MHz​(nPDTemp2+1)​

  本实验中设TIM2的定时周期nPDTemp2=72000/5000-1,预分频值OCPolarity2=999,TIM3的定时周期nPDTemp3=6399,预分频值OCPolarity3为0。即 T = 72000 5000 72 × 1 0 6 999 + 1 = 0.0002 s T= \frac{\frac{72000}{5000}}{ \frac{72\times 10^6}{999+1}}=0.0002s T=999+172×106​500072000​​=0.0002s   定时器共产生nPDTemp3+1=6400个脉冲,电机转过的角度为6400*1.8°=11520°,即电机转了32圈。   转动速度 w = 1.8 ° / 360 ° T = 25 ( r a d / s ) w = \frac{1.8°/360°}{T}=25 (rad/s) w=T1.8°/360°​=25(rad/s) 4.在32细分的情况下电机1rad/s和转1°需要的重装值为nPDTemp2=11.25,nPDTemp3=17.7778。

四、程序实现 1.main.c程序 #include "main.h" #include "sys.h" #include "usart1.h" #include "delay.h" #include "math.h" u16 t; u16 len; //接收到的数据长度 u16 times=0; char receive_data[60]; //接收到的字符 char state; //电机正反转标志位0正转,1反转 int speed,angle; //旋转速度,角度 int a=0; //判断是否接收到数据 int r,data=0; //用于数据转换 int type; //转换后的数据 extern u16 USART_RX_STA; /************************** * 函数名:delay * 描述 :延时函数 * 输入 :无 * 输出 :无 * 返回值:无 ****************************/ void delay()//延时 { int i,j; for(i=0;i0)//接收到的数据不是0 { TIM2_Master__TIM3_Slave_Configuration(speed,angle); // 配置TIM2和TIM3的重装值 ,改变电机转动速度和角度 delay_ms(20000);//电机保护 printf("\r\n 电机已旋转%d °,请输入下一次旋转角度,输入0返回初始模式; \r\n",angle); angle=0; Waiting_reception(); angle =type; }else{ if(angle==0)goto Initial_state;//返回初始状态 else { printf("\r\n 角度错误,已返回初始模式 \r\n"); goto Initial_state; } } } } /*********************************************反转模式********************************************************/ else{ if(type==1) { GPIO_ResetBits(GPIOA, GPIO_Pin_12);//电机反转 printf("\r\n 电机为正转模式,请输入旋转速度(rad/s),输入0返回初始模式 \r\n"); /*********************************************此模块用于配置速度参数********************************************************/ part2: Waiting_reception(); speed =type;//将接收到的数据给speed if(speed==0)goto Initial_state;//如果是0则返回初始模式 else{ if(speed>=15) { printf("\r\n旋转速度>15rad/s,请重新选择旋转速度。\r\n"); goto part2; }else printf("\r\n 电机旋转速度为%d rad/s,请输入旋转角度,输入0返回初始模式 \r\n",speed); } /*********************************************此模块用于配置角度参数********************************************************/ Waiting_reception(); angle =type;//将接收到的数据给type for(;;) { if(angle>0)//接收到的数据不是0 { TIM2_Master__TIM3_Slave_Configuration(speed,angle); // 配置TIM2和TIM3的重装值 ,改变电机转动速度和角度 delay_ms(20000);//电机保护 printf("\r\n 电机已旋转%d °,请输入下一次旋转角度,输入0返回初始模式;\r\n",angle); angle=0; Waiting_reception(); angle =type; }else{ if(angle==0)goto Initial_state;//返回初始状态 else { printf("\r\n 角度错误,已返回初始模式 \r\n"); goto Initial_state; } } } /****************************************************************************************************************************/ }else{//if(a!=0)&(a!=1) type=NULL; printf("\r\n 输入无效 \r\n"); goto Initial_state;//返回初始状态 } } } } 2.main.h程序 #ifndef _MAIN_H #define _MAIN_H #include #include #include #include #include #include "stm32f10x_tim.h" #include "timer.h" #endif 3.time.c程序 #include "timer.h" /************************** * 函数名:GPIO_Config * 描述 :无 * 输入 :无 * 输出 :无 * 调用 :主函数 * 返回值:无 ****************************/ void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能IOA RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3, ENABLE); //使能TIM2,TIM3 /* Timer2 Channel 1, PA0 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_11|GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //指定GPIO引脚可输出的最高频率为50MHZ GPIO_Init(GPIOA, &GPIO_InitStructure); // GPIO_ResetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向 GPIO_ResetBits(GPIOA, GPIO_Pin_2);//指定引脚输出低电平,此时灯全灭 使能 GPIO_SetBits(GPIOA, GPIO_Pin_11);//指定引脚输出低电平,此时灯全灭,方向 GPIO_SetBits(GPIOA, GPIO_Pin_12);//指定引脚输出低电平,此时灯全灭 使能 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //通用推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //指定GPIO引脚可输出的最高频率为50MHZ GPIO_Init(GPIOA, &GPIO_InitStructure); // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //通用推挽输出模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //指定GPIO引脚可输出的最高频率为50MHZ GPIO_Init(GPIOB, &GPIO_InitStructure); // GPIO_ResetBits(GPIOB, GPIO_Pin_12);//指定引脚输出低电平,此时灯全灭 使能 //GPIO_ResetBits GPIO_SetBits } //================================================================================ /************************** * 函数名:TIM2_Master__TIM3_Slave_Configuration * 描述 :主从定时器配置 * 输入 :电机转速speed,转角angle * 输出 :无 * 调用 :主函数 * 返回值:无 ****************************/ void TIM2_Master__TIM3_Slave_Configuration(u32 PulseFrequency, u32 pulse) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; u16 nPDTemp ; u16 pulse_number; float p=PulseFrequency; TIM_Cmd(TIM2, DISABLE); nPDTemp = (11.25/p); //TIM2重装值是11.25时1s转一圈(电机32细分下) pulse_number = (16.7778*pulse);//TIM3重装值是16.7778时转1°(电机32细分下) // 时基配置:配置PWM输出定时器——TIM2 /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = nPDTemp; //定时周期为nPDTemp TIM_TimeBaseStructure.TIM_Prescaler = 999; //预分频值1000,即f=72khz TIM_TimeBaseStructure.TIM_ClockDivision = 0; //时钟分频因子,会影响滤波器采样频率,与本实验无影响 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //指定重复计数器值 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 输出配置:配置PWM输出定时器——TIM2 /* PWM1 Mode configuration: Channel1 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //TIM 脉冲宽度调制模式 1 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //高电平有效 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出 TIM_OCInitStructure.TIM_Pulse = nPDTemp>>1;//50% //比较tim_ccr的值,输出脉冲发生跳变 TIM_OC1Init(TIM2, &TIM_OCInitStructure); //初始化 TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); //使能 TIMx 在 CCR1 上的预装载寄存器 TIM_ARRPreloadConfig(TIM2, ENABLE); //使能或者失能 TIMx 在 ARR 上的预装载寄存器 // 时基配置:配置脉冲计数寄存器——TIM3 TIM_TimeBaseStructure.TIM_Period = pulse_number; //0x1900是360°;//改变给电机的脉冲个数 TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 输出配置:配置输出比较非主动模式定时器——TIM3 // Output Compare Active Mode configuration: Channel1 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive; //输出比较非主动模式,(匹配时设置输出引脚为无效 电平,当计数值为比较/捕获寄存器值相同时,强制输出为低电平) TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0xFFFF; // 这里的配置值意义不大 TIM_OC1Init(TIM3, &TIM_OCInitStructure); // 配置TIM2为主定时器 // Select the Master Slave Mode TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable); //设置 TIM2 主/从模式并使能 // Master Mode selection TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); //使用更新事件作为触发输出 // 配置TIM3为从定时器 // Slave Mode selection: TIM3 TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Gated); //选择 TIM3为从模式 TIM_SlaveMode_Gated-当触发信号(TRGI)为高电平时计数器时钟使能 TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1); //选择 TIM3 输入触发源 TIM_TS_ITR1-TIM 内部触发 1 TIM_ITRxExternalClockConfig(TIM3, TIM_TS_ITR1);//设置 TIM3 内部触发为外部时钟模式 TIM_TS_ITR1-TIM 内部触发 1 TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); //使能TIM3 TIM 捕获/比较 1 中断源 TIM_Cmd(TIM2, ENABLE); TIM_Cmd(TIM3, ENABLE); } /**************************************************** * 函数名:Output_Pulse * 描述 :无 * 输入 :无 * 输出 :无 * 返回值:无 ******************************************************/ void Output_Pulse(u16 Num) { GPIO_ResetBits(GPIOA, GPIO_Pin_2);//指定引脚输出低电平,此时灯全灭 使能 TIM3->CCR1 = Num; TIM3->CNT = 0; TIM_Cmd(TIM3, ENABLE); TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); TIM_Cmd(TIM2, ENABLE); } /**************************************************** * 函数名:angle_set * 描述 :无 * 输入 :无 * 输出 :无 * 返回值:无 ******************************************************/ void angle_set(u8 dir,u8 angle) { if(dir==0) GPIO_ResetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向 else GPIO_SetBits(GPIOA, GPIO_Pin_1);//指定引脚输出低电平,此时灯全灭,方向 Output_Pulse(angle*6400); } 4.time.h程序 #ifndef __TIMER_H #define __TIMER_H #include "main.h" extern unsigned char Flag; extern unsigned char TIM2_Pulse_TIM3_Counter_OK; void GPIO_Config(void); void TIM2_Master__TIM3_Slave_Configuration(u32 PulseFrequency,u32 pulse); void Frequence_Setting(u32 PulseFrequency); void Output_Pulse(u16 Num); void angle_set(u8 dir,u8 angle); #endif 5.usart1.c程序 #include #include #include #include /****************************************************** * 函数名:USART1_Config * 描述 :USART1 GPIO 配置,工作模式配置 * 输入 :无 * 输出 : 无 * 调用 :外部调用 ***************************************************** */ void USART1_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; /* config USART1 clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); /* USART1 GPIO config */ /* Configure USART1 Tx (PA.09) as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Configure USART1 Rx (PA.10) as input floating */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); /* USART1 mode config */ USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No ; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE); } /****************************************************** * 函数名:fputc * 描述 :重定向c库函数printf到USART1 * 输入 :无 * 输出 :无 * 调用 :由printf调用 ***************************************************** */ int fputc(int ch, FILE *f) { /* 将Printf内容发往串口 */ USART_SendData(USART1, (unsigned char) ch); while (!(USART1->SR & USART_FLAG_TXE)); return (ch); } /*-------------------------------------------------------------------------------*/ /****************************************************** * 函数名:USART1_IRQHandler * 描述 :USART1中断服务函数 * 输入 :无 * 输出 :无 * 调用 :中断调用 ***************************************************** */ u8 Res; extern int a; u16 USART_RX_STA=0; //接收状态标记 u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. void USART1_IRQHandler(void) //串口1中断服务程序 { Res =USART_ReceiveData(USART1); //读取接收到的数据 if((USART_RX_STA&0x8000)==0)//接收未完成 { if(USART_RX_STA&0x4000)//接收到了0x0d { if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始 else USART_RX_STA|=0x8000; //接收完成了 } else //还没收到0X0D { if(Res==0x0d)USART_RX_STA|=0x4000; else { USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; USART_RX_STA++; if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收 } } } a=1; } #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS. OSIntExit(); #endif /******************* (C) COPYRIGHT 2012 WildFire Team *****END OF FILE************/ 6.usart1.h程序 #ifndef __USART1_H #define __USART1_H #include #include #define USART_REC_LEN 200 //定义最大接收字节数 200 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收 extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 extern u16 USART_RX_STA; //接收状态标记 int simple_atoi(char *source); void USART1_Config(void); #endif /* __USART1_H */

未完,待续… —————————————————————————— 源代码: 链接:https://pan.baidu.com/s/1PkmxkoeHfSfX06Gnj4Dr3A 提取码:eas7

—————————————————————————— 2020.8.15更新 1.程序跑飞问题已解决,有时间了整理一下,再传上来 2.引脚问题 此次开发板是一个引脚比较少的板子,不同开发板可能拥有的引脚不一样,但是相同名字的芯片引脚功能是一样的,具体可以查一下f103芯片手册。 (1)KEY0和KEY1本实验中没用到,不用管,是外界的一个按键 (2)A 0 、 A3 、A11 、A 12 是普通的IO口 (3)A 3—3.3V这一接法是严重有问题的(好像知道为啥当时外接的按键老是冒火花了) (4)PUL- DIR-不接引脚也可以(这里要打个问号,记得是这样,不敢百分百确定) (5)串口引脚在USART1.C程序里可以查,两个,RX TX,这里需要大家对着自己的板子检查一下看有没有程序里的这两个引脚,没有换个口,并改一下程序

所以 可以把引脚修改为 A 0——PUL+ A12——DIR+ A11——EAN+ GND——EAN-

引脚功能 A0控制电机转速 A3控制按键 A11控制电机是否为锁死状态 A12控制电机正反转

f103手册 链接:https://pan.baidu.com/s/1VkVxkj2XKzbpsjQd_UibJw 提取码:kx14



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3