利用STM32F103精确控制步进电机「建议收藏」

利用STM32F103精确控制步进电机「建议收藏」**利用STM32F103控制步进电机精确角度转动**欢迎使用Markdown编辑器你好!这是你第一次使用Markdown编辑器所展示的欢迎页。如果你想学习如何使用Markdown编辑器,可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,…

大家好,又见面了,我是你们的朋友全栈君。

利用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)f60

  • 转速单位: 转/ 分
  • 频率单位:赫兹
  • 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)7200060=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 脉冲/转 S1 S2 S3
NC NC ON ON ON
1 200 ON ON OFF
2/A 400 ON OFF ON
2/B 400 OFF ON ON
4 800 ON OFF OFF
8 1600 OFF ON OFF
16 3200 OFF OFF ON
32 6400 OFF OFF OFF

电流大小设定

Current(A) S4 S5 S6
0.5 ON ON ON
1.0 ON OFF ON
1.5 ON ON OFF
2.0 ON OFF OFF
2.5 OFF ON ON
2.8 OFF OFF ON
3.0 OFF ON OFF
3.5 OFF OFF OFF

三、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×106500072000=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;i<2000;i++)
	 for(j=0;j<1000;j++);
 }

 /************************** * 函数名:Waiting_receptio * 描述 :等待串口接收数据 * 输入 :无 * 输出 :无 * 返回值:无 ****************************/
void Waiting_reception(void)
{ 
   
	while(a==0)//等待数据发送完成
	{ 
   
		delay_ms(100);
		if(a==1)//接收到数据
		{ 
   
			if(USART_RX_STA&0x8000)//(串口接收用到了正点原子的例程)
			{ 
   
				len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
				r=len;				
				type=0;
				for(t=0;t<len;t++)//拷贝数据,将字符转换为十进制数
				{ 
   
					receive_data[t]=USART_RX_BUF[t];
					data=(int)receive_data[t]-48;
					r=r-1;
					type=type+data*(pow(10,r));
				}
				USART_RX_STA=0;
				a=0;
				delay_ms(500);
				break;
			}
		}
	}
}
/************************** * 函数名:KeyStates * 描述 :监测按键状态 * 输入 :无 * 输出 :无 * 返回值:0/1 ****************************/
u8 KeyStates()//按键状态
{ 
   
	static u8 i = 0;
	if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0)
	{ 
   
		delay();
		if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0)
			while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3)==0);
			i = ~i;
	}
	return i;
}
/**************** * 函数名:main * 描述 :主函数 * 输入 :无 * 输出 :无 ******************/
int main(void)
{ 
   
	NVIC_Configuration();			//中断初始化
	GPIO_Config();					//IO口初始化
	USART1_Config();				//串口初始化
    delay_init();	    	   	    //延时函数初始化 
	GPIO_ResetBits(GPIOA, GPIO_Pin_11);//A11置零 A11——EAN+
	GPIO_ResetBits(GPIOA, GPIO_Pin_12);//A12置零 A12——DIR+
	while(1)
	{ 
   
		delay_ms(100);
		Initial_state:		printf("\r\n 请选择正反转,正转输入0,反转输入1 (以新行作为结束标志)\r\n");	
		Waiting_reception();
		state=type;//将接收到的数据给type
		if(type==0)//电机正转
		{ 
   
			GPIO_SetBits(GPIOA, GPIO_Pin_12);//电机正转
			printf("\r\n 电机为正转模式,请输入旋转速度(rad/s),输入0返回初始模式 \r\n");
			
/*********************************************此模块用于配置速度参数********************************************************/

			part1:Waiting_reception();
			speed =type;//将接收到的数据给speed
			if(speed==0)goto Initial_state;//如果是0则返回初始模式
				else{ 
   
					if(speed>=15)
					{ 
   
						printf("\r\n 旋转速度>15rad/s,请重新选择旋转速度。\r\n");
						goto part1;
					}
						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(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 <stm32f10x.h>
#include <usart1.h>
#include <misc.h>
#include <nvic.h>
#include <stdio.h>
#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 <main.h>
#include <usart1.h>
#include <string.h>
#include <math.h>

/****************************************************** * 函数名: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 <main.h>
#include <stdio.h>

#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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/138067.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • linux iptable命令用法

    linux iptable命令用法iptables命令是Linux上常用的防火墙软件,是netfilter项目的一部分。可以直接配置,也可以通过许多前端和图形界面配置。语法iptables(选项)(参数)选项-t&lt;表&gt;:指定要操纵的表;-A:向规则链中添加条目;-D:从规则链中删除条目;-i:向规则链中插入条目;-R:替换规则链中的条目;-L:显示规则链中已有的条目;-F:清楚规则链中已…

  • 苹果手机数据转移到新手机_旧手机数据转移到新手机,一键免费传输

    苹果手机数据转移到新手机_旧手机数据转移到新手机,一键免费传输这款软件所有人都能用到建议收藏备用当我们换新手机时是不是很多数据需要转移很繁琐费劲电话号短信备注等等都想留着解决办法来了!!!今天推送的这款神器是腾讯旗下唯一一款零差评的app这款软件真正解决了我们平时更换手机遇到的所有痛点,没有GG无需会员软件名字:换机助手(适用于安卓iOS)01软件介绍现在随着互联网的发展,智能手机几乎人手必备,而且大家更换手机的频率越来越高,更换手机时候,…

  • api接口调用

    api接口调用api接口调用CURL是一个利用URL语法规定来传输文件和数据的工具,支持很多协议,如HTTP、FTP、TELNET等。最爽的是,PHP也支持CURL库。使用PHP的CURL库可以简单和有效

  • intelli idea2021激活码【在线注册码/序列号/破解码】

    intelli idea2021激活码【在线注册码/序列号/破解码】,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • 电赛练习之旋转倒立摆PID调节[通俗易懂]

    电赛练习之旋转倒立摆PID调节[通俗易懂]前言:在家准备电赛控制题,第一个选择的旋转倒立摆,结构和电路相对简单,对于新手比较友好。本人今年大二,自学的STM32和PID算法,本文算是对这个题目练习的记录吧,文章和程序有误的地方还请大家多多指教。一、机械结构考虑到正式比赛时需要自己搭建机械结构,我就没有直接购买现成的机械结构。关于机械结构还是平时接触太少了,随便在淘宝上买的不锈钢打孔支架作摆臂和旋转臂,最后发现传感器没法安放,强行用电机支架和胶带固定住。最困难的是怎么把电机和旋转臂连接得牢靠,最开始用的一个联轴器,发现转的猛了就会松动,想尽各种办

  • 阿里巴巴主流数据库连接池Druid入门

    阿里巴巴主流数据库连接池Druid入门一.数据库连接池的必要性(一).传统数据库连接模式的的步骤1.在主程序中创建连接2.进行sql操作3.关闭数据库连接(二).传统数据库连接模式存在的问题1.浪费时间:每次连接时都要验证登录和将conn加载到内存,2.不能大规模的访问数据库:当数据库访问人数过多时,占用大量系统资源,会导致服务器崩溃3.存在内存泄漏问题:每次连接都需要断开连接,如果不断开,程序运行结束,会有创建的连接对象存在内存中一直无法关闭,就会导致java内存…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号