STM32F103ZET6移植-电机2804-驱动板SimpleFOC Mini实现速度开环_位置开环控制(二、代码移植及功能实现)

一、工程实现

1.led模块

仅实现状态指示,这里不多做介绍。

2.新建time_open_control模块

  • 驱动板使能管脚

  • TIM4实现1ms中断一次,time4_count计数++

  • TIM2输出3路25KHz PWM

  • TIM2配置为中央对齐模式(和向上计数模式模式频率计算不同),频率 = 72M / ((预分频 + 1) × 2 × ARR )
    time_open_control.h

    #ifndef __TIME_OPEN_CONTROL_H
    #define __TIME_OPEN_CONTROL_H

    #include "system.h" //使用位带操作

    #define PWM_Period 1440 //在BLDCMotor.c文件中TIM_SetCompare4(TIM2, Tc*PWM_Period)中会使用

    #define MOTOR_ENABLE PCout(3) = 1
    #define MOTOR_DISABLE PCout(3) = 0

    extern uint32_t time4_count;

    void MOTOR_EN_PIN_Init(void);

    void TIM4_1MS_Init(void);
    void TIM2_CH1_CH3_CH4_PWM_Init(u16 per, u16 psc, u16 pulse1, u16 pulse2, u16 pulse3);

    #endif

time_open_control.c

复制代码
#include "system.h"
#include "time_open_control.h"

uint32_t time4_count = 0;

void MOTOR_EN_PIN_Init(void)
{
	GPIO_InitTypeDef MY_GPIO_Init; //定义结构体变量
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //在led.c中使能了,重复使能不影响
	
	MY_GPIO_Init.GPIO_Pin = GPIO_Pin_3; //开发板LED4复用了,不影响
	MY_GPIO_Init.GPIO_Mode = GPIO_Mode_Out_PP; //设置推挽输出模式
	MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
	GPIO_Init(GPIOC, &MY_GPIO_Init); 	   
	
	GPIO_ResetBits(GPIOC, GPIO_Pin_3); //电机disable,但LED4会被点亮
}

/***************************************************************************/

//功能:定时器2 通道1、3、4输出高有效的PWM(这里使用的是中央对齐模式)
//参数1:per 自动重装寄存器值
//参数2:psc 分频系数
//参数3:pulse1 占空比控制 第一路占空比为 = pulse1/per
//参数4:pulse2 占空比控制 第二路占空比为 = pulse2/per
//参数5:pulse3 占空比控制 第三路占空比为 = pulse3/per
//向上计数模式:
//频率 = 72M / ((预分频 + 1) × (ARR+1) )
//中央对齐模式:
//频率 = 72M / ((预分频 + 1) × 2 × ARR )	
void TIM2_CH1_CH3_CH4_PWM_Init(u16 per, u16 psc, u16 pulse1, u16 pulse2, u16 pulse3)
{
	GPIO_InitTypeDef MY_GPIO_Init;//定义结构体变量
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 
	TIM_OCInitTypeDef TIM_OCInitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能IO复用功能端口时钟(因为IO口需要重映射) 这里在TIM3_CH1_CH2_PWM_Init也有调用
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB端口时钟
	//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA端口时钟  在串口中已经使能过了
	
	//GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE); //由查表可知,PB10、PB11使用的是完全重映射管脚
	GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2, ENABLE); //由查表可知,PB10(CH3)、PB11(CH4)使用的也可以是重映射2管脚,以及使用PA0(CH1)
	
	//CH1
	MY_GPIO_Init.GPIO_Pin = GPIO_Pin_0; 
	MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
	MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
	GPIO_Init(GPIOA, &MY_GPIO_Init); 	   
	
	//CH3
	MY_GPIO_Init.GPIO_Pin = GPIO_Pin_10; 
	MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
	MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
	GPIO_Init(GPIOB, &MY_GPIO_Init); 	   

	//CH4
	MY_GPIO_Init.GPIO_Pin = GPIO_Pin_11; 
	MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
	MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
	GPIO_Init(GPIOB, &MY_GPIO_Init); 	   
	
	//初始化定时器参数
	TIM_DeInit(TIM2);
	TIM_TimeBaseStructure.TIM_Period = per; 
	TIM_TimeBaseStructure.TIM_Prescaler = psc; 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //一般配置为TIM_CKD_DIV1
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1; 
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);	

	//初始化PWM CH1输出参数
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM输出模式1
	TIM_OCInitStructure.TIM_Pulse = pulse1; 
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出高有效,9g舵机控制要求 
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);

	//初始化PWM CH3输出参数
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM输出模式1
	TIM_OCInitStructure.TIM_Pulse = pulse2; 
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出高有效,9g舵机控制要求 
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);

	//初始化PWM CH4输出参数
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM输出模式1
	TIM_OCInitStructure.TIM_Pulse = pulse3; 
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出高有效,9g舵机控制要求 
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
	TIM_OC4Init(TIM2, &TIM_OCInitStructure);

	//使能输出比较预装载寄存器
	TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
	TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
	TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);
	
	//使能 ARR 预装载寄存器
	TIM_ARRPreloadConfig(TIM2, ENABLE);
	
	//开启定时器
	TIM_Cmd(TIM2, ENABLE);	

	//TIM_CtrlPWMOutputs() 如果使用高级定时器输出PWM,还需要调用该函数使能主输出,否则不能输出PWM !!!
}


/***************************************************************************/
//1MS产生一次中断
void TIM4_1MS_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	//TIM4时钟使能
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
	
	//初始化NVIC,这里未配置中断优先级
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure); 
	
	TIM_TimeBaseInitStructure.TIM_Period = 1000-1; //1ms
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1; //72分频 = 1MHz
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure);
	
	//开启定时器中断,即使用定时器4的更新中断作为NVIC的中断源,如计数器到达设定自动重装载寄存器值时,触发更新中断
	TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
	TIM_Cmd(TIM4, ENABLE);
}

//定时器中断服务函数:只做一件事,计数器加一
void TIM4_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM4,TIM_IT_Update) == SET) //溢出中断
	{
		time4_count++;
	}
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除中断标志位
}

/***************************************************************************/

3.SysTick系统定时器

  • SysTick系统定时器新增SysTick_CountMode接口,用于开环控制(禁用延时函数)

  • 详情参考代码及注释
    SysTick.h

    #ifndef __SYSTICK_H
    #define __SYSTICK_H

    #include "system.h"

    void SysTick_Init(u8 SYSCLK);
    void delay_ms(u16 nms);
    void delay_us(u32 nus);

    void SysTick_CountMode(void);

    #endif

SysTick.c

复制代码
#include "SysTick.h"

static u8 fac_us = 0; //即延时1us需要的计数次数							  
static u16 fac_ms = 0; //即延时1ms需要的计数次数												

//使用延时函数需要先进行初始化
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //SysTick时钟源为HCLK的8分频,由时钟树图可以得知,HCLK时钟等于AHB时钟
	fac_us = SYSCLK/8; //SYSCLK = 72M为例,fac_us = 9即延时1us需要的计数次数为9							
	fac_ms = (u16)fac_us * 1000;		
}

/*******************************************************************************
* 函 数 名         : delay_us
* 函数功能		     : us延时,
* 输    入         : nus:要延时的us数
	注    意         : nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
* 输    出         : 无
*******************************************************************************/		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD = nus * fac_us; //将延时对应的预重装值写入LOAD寄存器  		 
	SysTick->VAL = 0x00; //清空当前数值寄存器VAL
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // SysTick定时器使能  
	do
	{
		temp = SysTick->CTRL; //读取CTRL寄存器
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达后退出   
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // SysTick定时器disable  
	SysTick->VAL = 0x00; //清空当前数值寄存器VAL
}

/*******************************************************************************
* 函 数 名         : delay_ms
* 函数功能		     : ms延时,
* 输    入         : nms:要延时的ms数
	注    意         :nms的值,SysTick->LOAD为24位寄存器,
					不要大于0xffffff*8*1000/SYSCLK对72M条件下,nms<=1864ms 
* 输    出         : 无
*******************************************************************************/
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD = (u32)nms * fac_ms; //只有这里不同,其他和delay_us延时相同
	SysTick->VAL = 0x00;							
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;	
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	
	SysTick->VAL = 0x00;       					 	    
} 

/*******************************************************************************
* 函 数 名         : SysTick_CountMode
* 函数功能		       : 让SysTick定时器从0xFFFFFF到0循环计数
* 输    入         : 无
*	注    意         : 在BLDCMotor.c中需要读取SysTick->VAL的值,它要求 SysTick 必须一直自由跑!
* 输    出         : 无
*******************************************************************************/
void SysTick_CountMode(void)
{
	SysTick->LOAD = 0xFFFFFF - 1; //set reload register
  SysTick->VAL  = 0;
  SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; //Enable SysTick Timer
}

4.usart模块改造,修改接收中断回调:

  • 中断回调中实现存储字符串,用于串口控制命令解析

  • 在不需要不需要选择MicroLIB情况下,适配代码防止编译器报错

  • fputc的实现兼容库函数操作、寄存器操作
    usart.h

    #ifndef __USART_H
    #define __USART_H

    #include "system.h"
    #include "stdio.h" //要实现int fputc(int ch,FILE *p) 原型,需要包含该头文件

    #define USART_REC_LEN 256

    /* 声明是全局变量,需要在外部文件使用 */
    extern unsigned char USART_RX_BUF[USART_REC_LEN];
    extern unsigned short USART_RX_STA;

    void USART1_Init(u32 bound);

    #endif

usart.c

复制代码
#include "usart.h"

/* 
1.在不需要不需要选择MicroLIB情况下,这些代码是为了骗过编译器报错(如果选择了MicroLIB,则不需要这段代码也可以使用printf) 
*/
#if 1
#pragma import(__use_no_semihosting)

//标准库需要的支持函数
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;

//定义_sys_exit避免使用半主机模式
void _sys_exit(int x) 
{ 
	x = x; 
} 
#endif

/* fputc的实现以下两种写法都可以,第一种是直接操作寄存器;第二种是通过调用库函数的方式 */
#if 1
int fputc(int ch, FILE *f)
{      
	while((USART1->SR & 0X40) == 0);
  USART1->DR = (u8)ch;      
	return ch;
}
#else
int fputc(int ch, FILE *p)  //函数默认的,函数原型不能改变,在使用printf函数时自动调用
{
	USART_SendData(USART1,(u8)ch);	
	while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); //USART_FLAG_TXE状态标志表示可以发送下一个数据了,发送数据寄存器已经空了(发送移位寄存器数据可能没有发完)
	return ch;
}
#endif


/******************************************************************************/
unsigned char USART_RX_BUF[USART_REC_LEN]; //接收buffer

/*
接收状态标志:
bit15 = 1:接收完成标志
bit14 = 1:接收到0x0D(\r 的 16 进制是:0x0D)
bit13~0:接收的字节数
*/
unsigned short USART_RX_STA = 0;   

//bound:参数为输入波特率
void USART1_Init(u32 bound)
{
	GPIO_InitTypeDef MY_GPIO_Init; //定义结构体变量
	USART_InitTypeDef USART_InitStructure; 
	NVIC_InitTypeDef NVIC_InitStructure;
	
	//默认引脚 PA9/PA10,没有重映射 → 不需要使能 AFIO 时钟!
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能串口1时钟
	
	MY_GPIO_Init.GPIO_Pin = GPIO_Pin_9; //TX -- PA9
	MY_GPIO_Init.GPIO_Mode = GPIO_Mode_AF_PP; //设置复用推挽输出模式
	MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
	GPIO_Init(GPIOA, &MY_GPIO_Init); 	 

	MY_GPIO_Init.GPIO_Pin = GPIO_Pin_10; //RX -- PA10
	MY_GPIO_Init.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置浮空输入
	MY_GPIO_Init.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
	GPIO_Init(GPIOA, &MY_GPIO_Init); 

	
	USART_InitStructure.USART_BaudRate = bound; //设置波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置数据位为8位 
	USART_InitStructure.USART_StopBits = USART_StopBits_1; //设置停止位为1位
	USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控 
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送使能和接收使能
	USART_Init(USART1, &USART_InitStructure);	
	USART_Cmd(USART1, ENABLE); //使能 USART1串口
	
	//清除USART_FLAG_TC发送完成标志位,这个是状态标志位,并非中断标志位
	USART_ClearFlag(USART1, USART_FLAG_TC); //清除USART_FLAG_TC发送完成标志位,这步最好是清除一下
	
	//设置串口中断类型并使能
	//USART_IT_RXNE ------ 接收数据寄存器非空中断,"我收到了一个字节数据,已经放进接收寄存器了,快来读!"
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //我们选择的是接收中断
	
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //在"stm32f10x.h"查看
#if 0	
/* 在一般测试中,抢占优先级2、响应优先级都设置为0,数值越小优先级越高 */
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 
#else
/* 在FOC电机控制中,抢占优先级、响应优先级都设置为3,数值越小优先级越高 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //响应优先级3	
#endif	
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	
}

#if 0
/*
这个中断函数只是测试,接收到的数据原样发回。
*/
void USART1_IRQHandler(void)
{
	u8 data = 0;
	
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //串口接收中断标志位,检查指定的 USART 中断发生与否,当调用接收函数时会被自动清除。
	{
		//当你调用 USART_ReceiveData() 读取数据时,硬件会自动清除 USART_IT_RXNE!
		data = USART_ReceiveData(USART1);
		USART_SendData(USART1, data); //将接收的数据发送出去
		//USART_FLAG_TC发送完成标志位,表示当移位寄存器的最后一位数据被发送到 TX 引脚,且总线空闲时,才置 1。表示数据已经完全发完,总线已经空闲。
		//USART_FLAG_TXE发送数据寄存器空,当 TDR 寄存器的数据被转移到移位寄存器后,立刻置 1,表示 "我可以写下一个数据了"。
		while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET); //USART_FLAG_TC发送完成标志位
		//while(USART_GetITStatus(USART1, USART_FLAG_TC) != SET); //开始错用了这个函数,导致中断程序在这里卡死!!!
	}
	USART_ClearFlag(USART1, USART_FLAG_TC); //清除USART_FLAG_TC发送完成标志位,这个是状态标志位,并非中断标志位
}	
#else
/*
串口1中断程序:每收到 1 个字节,就进一次这个中断函数,将接收数据存放在全局BUF中
1.将RX收到的字节存放到USART_RX_BUF中,如['data',...,'data','data','\0']
2.USART_RX_STA中bit15、bit14记录接收标志;bit13~0:接收的字节数
3.如发送:T10.5\r\n ,将T10.5存放在USART_RX_BUF中,结尾加上字符串结束符'\0'
*/
void USART1_IRQHandler(void)   
{
	unsigned char Res;
	
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //判断是否是接收中断
	{
		Res = USART_ReceiveData(USART1);	//读取接收到的一个字节
		if((USART_RX_STA&0x8000) == 0) //bit15 = 0,接收未完成
		{
			if(USART_RX_STA&0x4000) //已经接收到了0x0D,即接收到了回车 \r (16进制为0x0D)
			{
				if(Res != 0x0A) // \n 为(0x0A)
				{
					USART_RX_STA = 0; //接收错误,重新开始
				}
				else 
				{
					USART_RX_STA |= 0x8000;	//接收到了\n,将bit15 = 1(接收完成)
					//USART_RX_STA&0X3FFF 为接收的字节数
					USART_RX_BUF[USART_RX_STA&0X3FFF] = '\0'; //最后一个字节放'0',方便判断
				}
			}
			else //还没收到0x0D
			{	
				//如果当前字节是\r
				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; //接收数据超过接收buffer大小,接收错误,重新开始  
					}
				}		 
			}
		}
  }
}
#endif

5.SimpleFOC库移植:

  • SimpleFOC文件夹拷贝到工程路径下,添加SimpleFOC工程组,添加如下工程文件到工程组:
  • 引入头文件路径:
  • 这些文件开环控制暂不使用,先暂存在这个路径下:
  • 加入头文件依赖:
    FOCMotor.c
    #include "foc_utils.h"
    #include "FOCMotor.h"
    #include "BLDCmotor.h"
    foc_utils.c
    #include "foc_utils.h"
    #include "FOCMotor.h"
    #include "BLDCmotor.h"
    BLDCMotor.c
    #include "time_open_control.h" //是因为需要包含system.h 需要调用TIM_SetCompareX库函数
    #include "foc_utils.h"
    #include "FOCMotor.h"
    #include "BLDCmotor.h"

6.main.c函数:

大致流程:

  • 状态指示灯管脚配置初始化

  • 驱动板SimpleFOC Mini 使能管脚配置初始化

  • 串口1初始化

  • 配置TIM2输出PWM(此时不输出,调用TIM_SetCompareX时输出)

  • 配置TIM4定时器中断为1MS

  • 配置controller控制模式、voltage_limit、电机极对数pole_pairs等参数

  • 驱动板使能

  • SysTick切到CountMode模式

  • while循环(指示灯控制--接收控制命令--控制输出调用move函数)
    main.c

    /* 该代码已经全部调通,led状态闪烁,默认是速度开环模式,高速下电机可能会堵转,低速发热比较严重 */

    #include <stdlib.h>

    #include "system.h" //已经包含了"stm32f10x.h" 以及定义了位带操作的宏,后续只包含该头文件就可以了
    #include "SysTick.h" //使用delay_ms和delay_us

    #include "led.h" //Led_Init()对8个LED GPIO初始化, 状态指示灯需要使用
    #include "time_open_control.h" //三路PWM以及状态指示灯闪烁控制

    #include "usart.h" //使用printf打印以及接收串口命令

    #include "FOCMotor.h" //使用controller全局变量及Type_velocity_openloop等数据结构
    #include "BLDCmotor.h" //voltage_power_supply等全局变量

    /*
    1.速度开环模式下(弧度):
    T0.01(再小的精度没有测试了) ~ T60(目前测试下再大可能会堵转)
    默认T6.28:上电后以6.28rad/s的转速转动(1圈/秒)
    2.位置角度开环模式下(float的取值范围):
    默认T6.28:上电后顺时针转动1圈后回到起点
    T-6.28:逆时针转动1圈后回到起点
    */
    float target = 0.0;

    void commander_run(void)
    {
    if((USART_RX_STA&0x8000) != 0)
    {
    switch(USART_RX_BUF[0])
    {
    case 'H':
    printf("Speed Mode:T+-0.01~T+-60\r\n");
    printf("ag:T1.23\r\n");
    printf("Angle Mode:+-float\r\n");
    printf("ag:T-6.28\r\n");
    printf("D:Motor disable!\r\n");
    printf("E:Motor enable!\r\n");
    break;
    case 'T': //T6.28
    target = atof((const char *)(USART_RX_BUF + 1));
    printf("RX = %.4f\r\n", target);
    break;
    case 'D': //D
    MOTOR_DISABLE;
    printf("Motor disable!\r\n");
    break;
    case 'E': //E
    MOTOR_ENABLE;
    printf("Motor enable!\r\n");
    default:
    break;
    }
    USART_RX_STA = 0; //不用清除BUF,清除BUF状态,下次接收复写即可
    }
    }

    //开环控制最重要的参数就是voltage_limit
    //1、电机抖动转不起来把voltage_limit设置的大一点,
    //2、电机发热严重的把voltage_limit设置的小一点,
    //3、串口发送指令"T10",后面要有回车换行符
    //4、开环不是电机控制的常态,不要纠结太久。
    int main(void)
    {
    SysTick_Init(72); //72为SYSCLK delay_ms延时函数需要使用
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断分组2:2 在使用中断回调函数时都需要调用, 这里虽然没有使用中断,但还是留着

    复制代码
      Led_Init(); //使用LED1作为状态显示
      MOTOR_EN_PIN_Init(); //驱动板SimpleFOC Mini 使能管脚配置
      		
      USART1_Init(9600); //使用printf及接收串口控制命令
      
      //中央对齐模式:
      //频率f = 72M / ((预分频 + 1) * 2 * ARR )	
      //以25K赫兹 40us为例:f = 72M / ((0+1) * 2 * (1440-1+1)) = 72000000 / (1440 * 2) = 25000 Hz = 25KHz
      TIM2_CH1_CH3_CH4_PWM_Init(1440 - 1, 0, 0, 0, 0); //25KHz,此时不输出PWM
      TIM4_1MS_Init(); //interrupt per 1ms
      
      delay_ms(1000); //Wait for the system to stabilize
      
      /************************参数调整*********************************/
      controller = Type_velocity_openloop; //选择开环控制类型为速度模式
      //controller = Type_angle_openloop;  //选择开环控制类型为角度模式
      
      voltage_power_supply = 12; //V
      //voltage_limit = 2.5; //V,最大值需小于12/1.732=6.9。大功率航模电机设置的小一点0.5-1;小功率云台电机设置的大一点1-3
      voltage_limit = 3.0; //这里2.5V调整为3V
      velocity_limit = 20; //rad/s angleOpenloop() use it
      pole_pairs = 7; //电机的极对数(N-S极多少对)
      
      target = 6.28; //选择开环控制类型为速度模式为例,上电后以6.28rad/s的转速转动(1圈/秒)
      /******************************************************************/
      
      MOTOR_ENABLE; //驱动板SimpleFOC Mini 使能管脚
      printf("Control Mode(Speed Mode = 3 ,Angle Mode = 4):[%d].\r\n", controller);
    printf("Motor ready.\r\n");
      
      /*
      在BLDCMotor.c中需要读取SysTick->VAL的值,它要求 SysTick 必须一直自由跑!
      */
      SysTick_CountMode(); //后续code不能再调用delay_us()和delay_ms()函数
      
      while(1)
      {
      	//这个time4_count计数只是为了LED状态指示灯闪烁使用
      	if(time4_count >= 200)  //0.2s闪烁一次
      	{
      		time4_count = 0;
      		led1 = !led1;			
      	}
      	move(target);
      	commander_run();
      }

    }

相关推荐
LingLong_roar1 小时前
手搓温湿度仪(单片机普冉PY32F002AF15P6TU + 温湿度传感器 SHT40-AD1B-R2 + 0.96寸TFT IPS 显示屏)软件实现
单片机·嵌入式硬件
深圳市晨芯阳科技有限公司1 小时前
HC9623晨芯阳400mA带载、18V耐压、低压差快速响应LDO
单片机·嵌入式硬件·ldo线性稳压ic·深圳市晨芯阳科技有限公司
chengpei1472 小时前
电信创维E900盒子固件备份刷机
嵌入式硬件
星夜夏空9911 小时前
STM32单片机学习(10)——GPIO输入
stm32·单片机·学习
secondyoung13 小时前
Arm架构解析:Cortex-R系列架构概览
arm开发·单片机·嵌入式硬件·mcu·arm
FreakStudio15 小时前
开源分享|用MicroPython 做了个 AI 小鸡,它会长大,还记得我所有的情绪
python·单片机·嵌入式·面向对象·并行计算·电子diy·电子计算机
黑白园17 小时前
I2C_GPIO模拟 读取AS5600编码器数据
stm32·单片机·嵌入式硬件
羽获飞17 小时前
从零开始学嵌入式之STM32——34.ADC-模数转换
stm32·单片机·嵌入式硬件
csg110717 小时前
智慧养殖篇(四):猪场自动化饲喂与疫病预警
运维·单片机·嵌入式硬件·物联网·自动化